import fsExtra from 'fs-extra';
import { LocalDriver } from '#providers/drive/drivers/local';

/**
 * List item returned by the drive drivers
 */
export interface DriveListItem<T = any> {
    /**
     * Location of list item on disk which can be used in driver methods
     */
    location: string;

    /**
     * Flag to know if item represents file or directory
     */
    isFile: boolean;

    /**
     * Original list item returned from underlying driver
     */
    original: T;
}
  /**
   * List item returned from local disk driver
   */
  export interface LocalDriveListItem extends DriveListItem<fsExtra.Dirent> {}

export interface DirectoryListingContract<Driver extends DriverContract, T> extends AsyncIterable<T> {
    /**
     * Reference to the driver for which the listing was created.
     */
    driver: Driver;

    /**
     * Filter generated items of listing with the given predicate function.
     */
    // filter(predicate: (item: T, index: number, driver: Driver) => Promise<boolean> | boolean): DirectoryListingContract<Driver, T>;

    /**
     * Transform generated items of listing with the given mapper function.
     */
    // map<M>(mapper: (item: T, index: number, driver: Driver) => Promise<M> | M): DirectoryListingContract<Driver, M>;

    /**
     * Do recursive listing of items. Without the next function it will do listing of leaf nodes only.
     * For advanced usage you can pass the next function which will get as parameter current item and it should
     * return the next location for list or null if the recursion should stop and yield the current item.
     * For advanced usage you can also limit the depth of recursion using the second argument of next function.
     */
    // recursive(
    //     next?: (current: T, depth: number, driver: Driver) => Promise<string | null> | string | null,
    // ): DirectoryListingContract<Driver, T>;

    /**
     * Add a piping chain function which gets the current async iterable and returns
     * new async iterable with modified directory listing output.
     * Function this is bound to instance of driver for which the listing is generated.
     * This allows using async generator functions and reference the driver methods easily.
     * Piping will always return clone of the current instance and add the function
     * to the chain of new cloned instance only to prevent side effects.
     */
    // pipe<U>(fn: (this: Driver, source: AsyncIterable<T>) => AsyncIterable<U>): DirectoryListingContract<Driver, U>;

    /**
     * Get the final async iterable after passing directory listing through chain of piping functions modifying the output.
     */
    toIterable(): AsyncIterable<T>;

    /**
     * Convert directory listing to array.
     */
    toArray(): Promise<T[]>;
}

/**
 * Shape of the generic driver
 */
export interface DriverContract {
    /**
     * Name of the driver
     */
    name: string;

    /**
     * A boolean to find if the location path exists or not
     */
    exists(location: string): Promise<boolean>;

    /**
     * Remove a given location path
     */
    delete(location: string): Promise<void>;

    /**
     * Return a listing directory iterator for given location.
     * @experimental
     */
    list?(location: string): DirectoryListingContract<DriverContract, DriveListItem>;
}

/**
 * Shape of the local disk driver
 */
export interface LocalDriverContract extends DriverContract {
    name: 'local';

    /**
     * Reference to the underlying adapter. Which is fs-extra
     */
    adapter: typeof fsExtra;

    /**
     * Make path to a given file location
     */
    makePath(location: string): string;
}

// interface LocalDriverContract {
//     delete(): Promise<void>;
// }
export type LocalDriverConfig = {
    driver: 'local';
    // visibility: Visibility
    root: string;

    /**
     * Base path is always required when "serveFiles = true"
     */
    serveFiles?: boolean;
    basePath?: string;
};

/**
 * List of registered drivers. Drivers shipped via other packages
 * should merge drivers to this interface
 */
export interface DriversList {
    // [key: string]: {implementation : DriverContract, config: {}};
    local: {
        implementation: LocalDriver;
        config: {
            driver: string;
            visibility: string;
            root: string;
            serveFiles: boolean;
            basePath: string;
        };
    };

    // [key: string]: DriverContract;
    // local: LocalDriver;
    // fake: {
    //     implementation: LocalDriverContract;
    //     config: LocalDriverConfig;
    // };
}

export type DriveConfig = {
    disk: keyof DriversList;
    // disks: {
    //     [name: string]: {
    //         driver: DriverContract;
    //     };
    // };
    disks: {
        [name: string]: {
            [K in keyof DriversList]: DriversList[K]['config'] & {
                driver: K;
                visibility: string;
                root: string;
                serveFiles: boolean;
                basePath: string;
            };
        }[keyof DriversList];
    };
};
