import type { ApplicationService } from '@adonisjs/core/types';
import { Manager } from '@poppinss/manager';
import { Exception } from '@poppinss/utils';
import { DriverContract, DriversList, DriveConfig, DriveListItem, DirectoryListingContract, LocalDriverConfig } from './types/drive.js';
// import { LocalDriver } from './drivers/local.js';
import { LocalDriver } from '../drivers/local.js';

type test = {
    [P in keyof DriversList]: DriversList[P];
};
// type DriveMappings = {
//     local: string
//     fake: string
//   }

// interface DriversList {
//     local: {
//         implementation: LocalDriverContract;
//         config: LocalDriverConfig;
//     };
// }

//   type DriverConfig = {
//     disk: keyof DriversList
//     disks: {
//       [K in keyof DriversList]: any
//     }
//   }

// const mailerConfig: DriveConfig = {
//     disk: 'local',

//     disks: {       
//         local: {
//             driver: 'local',
//             root: '',
//             visibility: '',
//             serveFiles: false,
//             basePath: '',
//         },

//         //   'fake': {
//         //     driver: 'fake',
//         //     root: '',
//         //   },
//     },
// };

export default class DriveManager extends Manager<
    ApplicationService,
    DriverContract,
    DriverContract,
    {
        [P in keyof DriversList]: DriversList[P]['implementation'];
    }
> {
    protected singleton = true;
    private config;
    /**
     * Find if drive is ready to be used
     */
    private isReady: boolean;

    protected getDefaultMappingName() {
        return this.config.disk; // "local"
    }

    protected getMappingConfig(mappingName: string) {
        return this.config.disks[mappingName];
    }

    protected getMappingDriver(mappingName: string) {
        return this.config.disks[mappingName].driver;
    }

     /**
     * Make instance of the local driver
     */
    //  protected createLocal(diskName: keyof DriversList, config: LocalDriverConfig) {
    protected createLocal(diskName: keyof DriversList, config: LocalDriverConfig) {
        // const { LocalDriver } = await import('../drivers/local.js');       
        return new LocalDriver(diskName, config);
        // return new LocalDriver(config);
    }

    constructor(application: ApplicationService, config: DriveConfig) {
        super(application);
        this.config = config;

        /**
         * Find if drive is ready to be used
         */
        this.isReady = false;

        this.validateConfig();
    }
    /**
     * Validate config
     */
    private validateConfig() {
        if (!this.config) {
            return;
        }
        // const validator = new utils_1.ManagerConfigValidator(this.config, 'drive', 'config/drive');
        // validator.validateDefault('disk');
        // validator.validateList('disks', 'disk');
        this.isReady = true;
    }

    /**
     * Resolve instance for a disk
     */
    use(disk?: keyof test): DriversList[keyof DriversList]['implementation'] {
        if (!this.isReady) {
            throw new Exception('Missing configuration for drive. Visit https://bit.ly/2WnR5j9 for setup instructions', {
                status: 500,
                code: 'E_MISSING_DRIVE_CONFIG',
            });
        }
        disk = disk || this.getDefaultMappingName(); //#local'
        // if (this.fakeDrive.isFaked(disk)) {
        //     return this.fakeDrive.use(disk);
        // }
        return super.use(disk);
    }

    /**
     * A boolean to find if the location path exists or not
     */
    exists(location: string): Promise<boolean> {
        const driver = this.use();
        return driver.exists(location);
    }

    /**
     * Remove a given location path
     */
    delete(location: string): Promise<void> {
        const driver = this.use();
        return driver.delete(location);
    }

    /**
     * Return a listing directory iterator for given location.
     */
    list(location: string): DirectoryListingContract<DriverContract, DriveListItem> {
        const driver = this.use();
        if (typeof driver.list !== 'function') {
            throw new Exception(`List is not supported by the "${driver.name}" driver.`, {
                status: 500,
                code: 'E_LIST_NOT_SUPPORTED',
            });
        }
        return driver.list(location);
    }

     /**
     * Move a given location path from the source to the desination.
     * The missing intermediate directories will be created (if required)
     */
     move(source: string, ...args: string[]): Promise<void>{
        const driver = this.use();
        return driver.move(source, args[0]);
     }

      /**
     * Make absolute path to a given location
     */
    public makePath(location: string): string {
        const driver = this.use();
        return driver.makePath(location);
    }
}
