"use strict";
/*
 * @adonisjs/drive
 *
 * (c) Harminder Virk <virk@adonisjs.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.DriveManager = void 0;
/// <reference path="../../adonis-typings/index.ts" />
const manager_1 = require("@poppinss/manager");
const utils_1 = require("@poppinss/utils");
const Fake_1 = require("../Fake");
/**
 * Drive manager exposes the API to resolve disks and extend by
 * adding custom drivers
 */
class DriveManager extends manager_1.Manager {
    constructor(application, router, logger, config) {
        super(application);
        this.application = application;
        this.router = router;
        this.logger = logger;
        this.config = config;
        /**
         * Find if drive is ready to be used
         */
        this.isReady = false;
        /**
         * The fake callback
         */
        this.fakeCallback = (_, disk, config) => {
            const { FakeDriver } = require('../Drivers/Fake');
            return new FakeDriver(disk, config, this.router);
        };
        /**
         * Reference to the fake drive
         */
        this.fakeDrive = new Fake_1.FakeDrive();
        /**
         * Cache all disks instances
         */
        this.singleton = true;
        /**
         * Reference to registered fakes
         */
        this.fakes = this.fakeDrive.fakes;
        this.validateConfig();
    }
    /**
     * Validate config
     */
    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;
    }
    /**
     * Returns the default mapping name
     */
    getDefaultMappingName() {
        return this.config.disk;
    }
    /**
     * Returns config for a given mapping
     */
    getMappingConfig(diskName) {
        return this.config.disks[diskName];
    }
    /**
     * Returns the name of the drive used by a given mapping
     */
    getMappingDriver(diskName) {
        return this.getMappingConfig(diskName)?.driver;
    }
    /**
     * Make instance of the local driver
     */
    createLocal(diskName, config) {
        const { LocalDriver } = require('../Drivers/Local');
        return new LocalDriver(diskName, config, this.router);
    }
    /**
     * Fake default or a named disk
     */
    fake(disks) {
        disks = disks || this.getDefaultMappingName();
        const disksToFake = Array.isArray(disks) ? disks : [disks];
        disksToFake.forEach((disk) => {
            if (!this.fakeDrive.isFaked(disk)) {
                this.logger.trace({ disk: disk }, 'drive faking disk');
                this.fakeDrive.fakes.set(disk, this.fakeCallback(this, disk, this.getMappingConfig(disk)));
            }
        });
        return this.fakeDrive;
    }
    /**
     * Restore the fake for the default or a named disk
     */
    restore(disks) {
        disks = disks || this.getDefaultMappingName();
        const disksToRestore = Array.isArray(disks) ? disks : [disks];
        disksToRestore.forEach((disk) => {
            if (this.fakeDrive.isFaked(disk)) {
                this.logger.trace({ disk: disk }, 'drive restoring disk fake');
                this.fakeDrive.restore(disk);
            }
        });
    }
    /**
     * Restore all fakes1
     */
    restoreAll() {
        this.fakeDrive.fakes = new Map();
    }
    /**
     * Resolve instance for a disk
     */
    use(disk) {
        if (!this.isReady) {
            throw new utils_1.Exception('Missing configuration for drive. Visit https://bit.ly/2WnR5j9 for setup instructions', 500, 'E_MISSING_DRIVE_CONFIG');
        }
        disk = disk || this.getDefaultMappingName();
        if (this.fakeDrive.isFaked(disk)) {
            return this.fakeDrive.use(disk);
        }
        return super.use(disk);
    }
    /**
     * Register a custom fake implementation
     */
    setFakeImplementation(callback) {
        this.fakeCallback = callback;
    }
    /**
     * Returns the file contents as a buffer. The buffer return
     * value allows you to self choose the encoding when
     * converting the buffer to a string.
     */
    async get(location, ...args) {
        return this.use().get(location, ...args);
    }
    /**
     * Returns the file contents as a stream
     */
    async getStream(location, ...args) {
        return this.use().getStream(location, ...args);
    }
    /**
     * A boolean to find if the location path exists or not
     */
    exists(location, ...args) {
        return this.use().exists(location, ...args);
    }
    /**
     * Returns the location path visibility
     */
    async getVisibility(location, ...args) {
        return this.use().getVisibility(location, ...args);
    }
    /**
     * Returns the location path stats
     */
    async getStats(location, ...args) {
        return this.use().getStats(location, ...args);
    }
    /**
     * Returns a signed URL for a given location path
     */
    getSignedUrl(location, ...args) {
        return this.use().getSignedUrl(location, ...args);
    }
    /**
     * Returns a URL for a given location path
     */
    getUrl(location, ...args) {
        return this.use().getUrl(location, ...args);
    }
    /**
     * Write string|buffer contents to a destination. The missing
     * intermediate directories will be created (if required).
     */
    put(location, ...args) {
        return this.use().put(location, ...args);
    }
    /**
     * Write a stream to a destination. The missing intermediate
     * directories will be created (if required).
     */
    putStream(location, ...args) {
        return this.use().putStream(location, ...args);
    }
    /**
     * Not supported
     */
    setVisibility(location, ...args) {
        return this.use().setVisibility(location, ...args);
    }
    /**
     * Remove a given location path
     */
    delete(location, ...args) {
        return this.use().delete(location, ...args);
    }
    /**
     * Copy a given location path from the source to the desination.
     * The missing intermediate directories will be created (if required)
     */
    copy(source, ...args) {
        return this.use().copy(source, ...args);
    }
    /**
     * Move a given location path from the source to the desination.
     * The missing intermediate directories will be created (if required)
     */
    move(source, ...args) {
        return this.use().move(source, ...args);
    }
    /**
     * Return a listing directory iterator for given location.
     */
    list(location) {
        const driver = this.use();
        if (typeof driver.list !== 'function') {
            throw new utils_1.Exception(`List is not supported by the "${driver.name}" driver.`, 500, 'E_LIST_NOT_SUPPORTED');
        }
        return driver.list(location);
    }
}
exports.DriveManager = DriveManager;
