import { storageConfig } from '../../config/storage.js';
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
import { LocalDriver } from './drivers/Local.js';
import { StorageDiskRegistry } from './StorageDiskRegistry.js';
import { StorageDriverRegistry } from './StorageDriverRegistry.js';
const normalizers = {
    local: (raw) => ({
        root: String(raw['root'] ?? ''),
        url: typeof raw['url'] === 'string' ? raw['url'] : undefined,
    }),
    s3: (raw) => ({
        bucket: String(raw['bucket'] ?? ''),
        region: String(raw['region'] ?? ''),
        accessKeyId: String(raw['accessKeyId'] ?? raw['key'] ?? ''),
        secretAccessKey: String(raw['secretAccessKey'] ?? raw['secret'] ?? ''),
        endpoint: typeof raw['endpoint'] === 'string' ? raw['endpoint'] : undefined,
        usePathStyle: Boolean(raw['usePathStyle'] ?? raw['usePathStyleUrl'] ?? false),
    }),
    r2: (raw) => ({
        bucket: String(raw['bucket'] ?? ''),
        region: typeof raw['region'] === 'string' ? raw['region'] : undefined,
        accessKeyId: String(raw['accessKeyId'] ?? raw['key'] ?? ''),
        secretAccessKey: String(raw['secretAccessKey'] ?? raw['secret'] ?? ''),
        endpoint: typeof raw['endpoint'] === 'string' ? raw['endpoint'] : undefined,
        url: typeof raw['url'] === 'string' ? raw['url'] : undefined,
    }),
    gcs: (raw) => ({
        bucket: String(raw['bucket'] ?? ''),
        projectId: typeof raw['projectId'] === 'string' ? raw['projectId'] : undefined,
        keyFile: typeof raw['keyFile'] === 'string' ? raw['keyFile'] : undefined,
        url: typeof raw['url'] === 'string' ? raw['url'] : undefined,
    }),
};
const normalizeDiskConfig = (driverName, raw) => {
    return normalizers[driverName]?.(raw) ?? raw;
};
const resolveDiskConfigFromRegistry = (name) => {
    const selected = String(name ?? '').trim();
    const hasSelection = name !== undefined && selected.length > 0;
    const registryKey = hasSelection ? selected : 'default';
    if (!StorageDiskRegistry.has(registryKey))
        return undefined;
    return StorageDiskRegistry.get(registryKey);
};
const resolveDiskConfigFromStorageConfig = (name) => {
    const maybeGetDriverConfig = storageConfig
        .getDriverConfig;
    if (typeof maybeGetDriverConfig !== 'function')
        return undefined;
    // Important: call as a method to preserve `this` binding.
    return storageConfig.getDriverConfig(name);
};
const resolveDiskConfigFromLegacyConfig = (name) => {
    const selected = String(name ?? '').trim();
    const hasSelection = name !== undefined && selected.length > 0;
    const drivers = storageConfig.drivers ?? {};
    const defaultName = String(storageConfig.default ?? 'local').trim();
    const requestedName = hasSelection ? selected : defaultName;
    const diskName = requestedName === 'default' ? defaultName : requestedName;
    if (diskName !== '' && Object.prototype.hasOwnProperty.call(drivers, diskName)) {
        return drivers[diskName];
    }
    if (hasSelection && selected !== 'default') {
        throw ErrorFactory.createConfigError(`Storage disk not configured: ${diskName}`);
    }
    return (drivers['local'] ??
        Object.values(drivers)[0]);
};
export const Storage = Object.freeze({
    getDisk(name) {
        const config = resolveDiskConfigFromRegistry(name) ??
            resolveDiskConfigFromStorageConfig(name) ??
            resolveDiskConfigFromLegacyConfig(name);
        if (config === undefined) {
            throw ErrorFactory.createConfigError('Storage: no disks are configured');
        }
        const driverName = String(config['driver'] ?? '')
            .trim()
            .toLowerCase();
        if (driverName === 'local') {
            return { driver: LocalDriver, config: normalizeDiskConfig('local', config) };
        }
        const entry = StorageDriverRegistry.get(driverName);
        if (entry === undefined) {
            if (driverName === 's3' || driverName === 'r2' || driverName === 'gcs') {
                throw ErrorFactory.createConfigError(`Storage driver not registered: ${driverName} (run \`zin add storage:${driverName}\` / \`npm i @zintrust/storage-${driverName}\`)`);
            }
            throw ErrorFactory.createValidationError('Storage: unsupported disk driver', {
                driver: config['driver'],
            });
        }
        const normalizedConfig = typeof entry.normalize === 'function'
            ? entry.normalize(config)
            : normalizeDiskConfig(driverName, config);
        return { driver: entry.driver, config: normalizedConfig };
    },
    async put(disk, path, contents) {
        const d = Storage.getDisk(disk);
        const driver = d.driver;
        if (typeof driver.put !== 'function') {
            throw ErrorFactory.createConfigError('Storage: driver is missing put()');
        }
        return driver.put(d.config, path, contents);
    },
    async get(disk, path) {
        const d = Storage.getDisk(disk);
        const driver = d.driver;
        if (typeof driver.get !== 'function') {
            throw ErrorFactory.createConfigError('Storage: driver is missing get()');
        }
        return driver.get(d.config, path);
    },
    async exists(disk, path) {
        const d = Storage.getDisk(disk);
        const driver = d.driver;
        if (typeof driver.exists !== 'function')
            return true;
        return Boolean(await driver.exists(d.config, path));
    },
    async delete(disk, path) {
        const d = Storage.getDisk(disk);
        const driver = d.driver;
        if (typeof driver.delete !== 'function')
            return;
        await driver.delete(d.config, path);
    },
    url(disk, path) {
        const d = Storage.getDisk(disk);
        const driver = d.driver;
        const url = typeof driver.url === 'function' ? driver.url(d.config, path) : undefined;
        if (typeof url !== 'string' || url.trim() === '') {
            throw ErrorFactory.createConfigError('Storage: driver cannot build url()');
        }
        return url;
    },
    async tempUrl(disk, path, options) {
        const d = Storage.getDisk(disk);
        const driver = d.driver;
        if (typeof driver.tempUrl === 'function') {
            return driver.tempUrl(d.config, path, options);
        }
        const url = typeof driver.url === 'function' ? driver.url(d.config, path) : undefined;
        if (typeof url !== 'string' || url.trim() === '') {
            throw ErrorFactory.createConfigError('Storage: driver does not support tempUrl()');
        }
        return url;
    },
});
export default Storage;
export { StorageDiskRegistry } from './StorageDiskRegistry.js';
