/**
 * Secrets Toolkit (Core)
 *
 * Internal framework module intended for CLI usage.
 * It may use Node APIs (via node-singletons) and should not be used by runtime apps.
 */
import { Env } from '../../config/env.js';
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
import { EnvFile } from '../Secrets/EnvFile.js';
import { Manifest } from '../Secrets/Manifest.js';
import { AwsSecretsManager } from '../Secrets/providers/AwsSecretsManager.js';
import { CloudflareKv } from '../Secrets/providers/CloudflareKv.js';
const resolveDefaultEnvFile = () => Env.ZINTRUST_ENV_FILE;
const resolveDefaultManifest = () => Env.ZINTRUST_SECRETS_MANIFEST;
const resolveDefaultInFile = () => Env.ZINTRUST_ENV_IN_FILE;
const resolveSecretsProvider = () => {
    return Env.ZINTRUST_SECRETS_PROVIDER;
};
const resolveProvider = (provider) => {
    if (provider === 'aws' || provider === 'cloudflare')
        return provider;
    throw ErrorFactory.createCliError('Missing/invalid provider. Use --provider aws|cloudflare or set ZINTRUST_SECRETS_PROVIDER.');
};
const resolveOutFile = (outFile) => outFile ?? resolveDefaultEnvFile();
const resolveManifestPath = (manifestPath) => manifestPath ?? resolveDefaultManifest();
const resolveInFile = (inFile) => inFile ?? resolveDefaultInFile();
const pullAws = async (manifest) => {
    const client = AwsSecretsManager.createFromEnv();
    const entries = Object.entries(manifest.keys).filter(([, spec]) => spec.aws !== undefined);
    const pairs = await Promise.all(entries.map(async ([envKey, spec]) => {
        const aws = spec.aws;
        if (aws === undefined)
            return null;
        const value = await client.getValue(aws.secretId, aws.jsonKey);
        return value === null ? null : [envKey, value];
    }));
    const resolved = {};
    for (const pair of pairs) {
        if (pair)
            resolved[pair[0]] = pair[1];
    }
    return resolved;
};
const pullCloudflare = async (manifest) => {
    const client = CloudflareKv.createFromEnv();
    const entries = Object.entries(manifest.keys).filter(([, spec]) => spec.cloudflare !== undefined);
    const pairs = await Promise.all(entries.map(async ([envKey, spec]) => {
        const cf = spec.cloudflare;
        if (cf === undefined)
            return null;
        const value = await client.getValue(cf.key, cf.namespaceId);
        return value === null ? null : [envKey, value];
    }));
    const resolved = {};
    for (const pair of pairs) {
        if (pair)
            resolved[pair[0]] = pair[1];
    }
    return resolved;
};
const pushAws = async (manifest, env, dryRun) => {
    const client = AwsSecretsManager.createFromEnv();
    const entries = Object.entries(manifest.keys).filter(([, spec]) => spec.aws !== undefined);
    const pushed = await Promise.all(entries.map(async ([envKey, spec]) => {
        const aws = spec.aws;
        if (aws === undefined)
            return null;
        const value = env[envKey];
        if (typeof value !== 'string')
            return null;
        if (!dryRun)
            await client.putValue(aws.secretId, value);
        return envKey;
    }));
    return pushed.filter((k) => typeof k === 'string');
};
const pushCloudflare = async (manifest, env, dryRun) => {
    const client = CloudflareKv.createFromEnv();
    const entries = Object.entries(manifest.keys).filter(([, spec]) => spec.cloudflare !== undefined);
    const pushed = await Promise.all(entries.map(async ([envKey, spec]) => {
        const cf = spec.cloudflare;
        if (cf === undefined)
            return null;
        const value = env[envKey];
        if (typeof value !== 'string')
            return null;
        if (!dryRun)
            await client.putValue(cf.key, value, cf.namespaceId);
        return envKey;
    }));
    return pushed.filter((k) => typeof k === 'string');
};
export const SecretsToolkit = Object.freeze({
    async pull(options) {
        const provider = resolveProvider(options.provider ?? resolveSecretsProvider());
        const outFile = resolveOutFile(options.outFile);
        const manifestPath = resolveManifestPath(options.manifestPath);
        const manifest = await Manifest.load({ cwd: options.cwd, path: manifestPath, provider });
        const resolved = provider === 'aws' ? await pullAws(manifest) : await pullCloudflare(manifest);
        if (options.dryRun === true)
            return { outFile, keys: Object.keys(resolved) };
        await EnvFile.write({ cwd: options.cwd, path: outFile, values: resolved, mode: 'overwrite' });
        return { outFile, keys: Object.keys(resolved) };
    },
    async push(options) {
        const provider = resolveProvider(options.provider ?? resolveSecretsProvider());
        const inFile = resolveInFile(options.inFile);
        const manifestPath = resolveManifestPath(options.manifestPath);
        const manifest = await Manifest.load({ cwd: options.cwd, path: manifestPath, provider });
        const env = await EnvFile.read({ cwd: options.cwd, path: inFile });
        const dryRun = options.dryRun === true;
        const keys = provider === 'aws'
            ? await pushAws(manifest, env, dryRun)
            : await pushCloudflare(manifest, env, dryRun);
        return { inFile, keys };
    },
    doctor(options) {
        const provider = resolveProvider(options.provider ?? resolveSecretsProvider());
        const missing = provider === 'aws' ? AwsSecretsManager.doctorEnv() : CloudflareKv.doctorEnv();
        return { provider, ok: missing.length === 0, missing };
    },
});
export default SecretsToolkit;
