"use strict";
/*
 * @poppinss/manager
 *
 * (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.Manager = void 0;
/**
 * The simplest implementation of capitalizing a string
 */
const capitalize = (value) => {
    if (!value) {
        return value;
    }
    return value.charAt(0).toUpperCase() + value.slice(1);
};
/**
 * Manager class implements the Builder pattern to make instance of similar
 * implementations using a fluent API vs importing each class by hand.
 *
 * This module is used extensively in AdonisJs. For example: `Mail`, `Sessions`,
 * `Auth` and so on.
 */
class Manager {
    constructor(application) {
        this.application = application;
        /**
         * Mappings cache (if caching is enabled)
         */
        this.mappingsCache = new Map();
        /**
         * A cache to store the function names for initiating driver instances.
         */
        this.driverCreatorNames = new Map();
        /**
         * List of drivers added at runtime
         */
        this.extendedDrivers = {};
    }
    /**
     * Returns the value saved inside cache, this method will check for
     * `cacheDrivers` attribute before entertaining the cache
     */
    getFromCache(name) {
        return this.mappingsCache.get(name) || null;
    }
    /**
     * Saves value to the cache with the driver name. This method will check for
     * `cacheDrivers` attribute before entertaining the cache.
     */
    saveToCache(name, value) {
        if (this.singleton) {
            this.mappingsCache.set(name, value);
        }
    }
    /**
     * Make the extended driver instance and save it to cache (if enabled)
     */
    makeExtendedDriver(mappingName, driver, config) {
        const value = this.wrapDriverResponse(mappingName, this.extendedDrivers[driver](this, mappingName, config));
        this.saveToCache(mappingName, value);
        return value;
    }
    /**
     * Returns the creator function name for a given driver.
     */
    getDriverCreatorName(driver) {
        if (!this.driverCreatorNames.has(driver)) {
            this.driverCreatorNames.set(driver, `create${capitalize(driver.replace(/-\w|_\w/g, (g) => g.substr(1).toUpperCase()))}`);
        }
        return this.driverCreatorNames.get(driver);
    }
    /**
     * Make the custom driver instance by checking for function on the
     * parent class.
     *
     * For example: `stmp` as the driver name will look for `createSmtp`
     * method on the parent class.
     */
    makeDriver(mappingName, driver, config) {
        const driverCreatorName = this.getDriverCreatorName(driver);
        /**
         * Raise error when the parent class doesn't implement the function
         */
        if (typeof this[driverCreatorName] !== 'function') {
            throw new Error(`"${driver}" driver is not supported by "${this.constructor.name}"`);
        }
        const value = this.wrapDriverResponse(mappingName, this[driverCreatorName](mappingName, config));
        this.saveToCache(mappingName, value);
        return value;
    }
    /**
     * Optional method to wrap the driver response
     */
    wrapDriverResponse(_, value) {
        return value;
    }
    use(name) {
        const mappingName = name || this.getDefaultMappingName();
        const cached = this.getFromCache(mappingName);
        if (cached) {
            return cached;
        }
        /**
         * Ensure that driver exists for a given mapping
         */
        const driver = this.getMappingDriver(mappingName);
        if (!driver) {
            throw new Error(`Make sure to define driver for "${mappingName}" mapping`);
        }
        /**
         * Making the extended driver
         */
        if (this.extendedDrivers[driver]) {
            return this.makeExtendedDriver(mappingName, driver, this.getMappingConfig(mappingName));
        }
        /**
         * Making the predefined driver
         */
        return this.makeDriver(mappingName, driver, this.getMappingConfig(mappingName));
    }
    release(name) {
        this.mappingsCache.delete(name);
    }
    /**
     * Extend by adding new driver. The compositon of driver
     * is the responsibility of the callback function
     */
    extend(name, callback) {
        this.extendedDrivers[name] = callback;
    }
}
exports.Manager = Manager;
