/**
 * Unified Secrets Management Layer
 * Abstracts secrets retrieval across different cloud platforms
 * Supports: AWS Secrets Manager, Parameter Store, Cloudflare KV, Deno env
 */
import { Env } from './env.js';
import { Logger } from './logger.js';
import { ErrorFactory } from '../exceptions/ZintrustError.js';
let instance;
function pruneCache(cache, maxEntries) {
    if (cache.size <= maxEntries)
        return;
    const now = Date.now();
    for (const [key, entry] of cache.entries()) {
        if (entry.expiresAt <= now)
            cache.delete(key);
    }
    // If still large, drop oldest entries (Map preserves insertion order)
    while (cache.size > maxEntries) {
        const next = cache.keys().next();
        if (next.done === true)
            break;
        cache.delete(next.value);
    }
}
/**
 * Get secret value from appropriate backend
 */
async function runGetSecret(config, cache, key, options) {
    // Check cache first
    const cached = cache.get(key);
    if (cached !== undefined && cached.expiresAt > Date.now()) {
        return cached.value;
    }
    // Opportunistic cleanup if cache has grown
    pruneCache(cache, 500);
    let value;
    switch (config.platform) {
        case 'aws':
            value = await getFromAWSSecretsManager(key);
            break;
        case 'cloudflare':
            value = await getFromCloudflareKV(config, key);
            break;
        case 'deno':
            value = await getFromDenoEnv(key);
            break;
        case 'local':
        default:
            value = await getFromEnv(key);
    }
    // Cache the value
    const ttl = options?.cacheTtl ?? 3600000; // 1 hour default
    cache.set(key, {
        value,
        expiresAt: Date.now() + ttl,
    });
    pruneCache(cache, 500);
    return value;
}
/**
 * Set secret value
 */
async function runSetSecret(config, cache, key, value, options) {
    switch (config.platform) {
        case 'aws':
            await setInAWSSecretsManager(key, value, options);
            break;
        case 'cloudflare':
            await setInCloudflareKV(config, key, value, options);
            break;
        case 'deno':
            throw ErrorFactory.createConfigError('Cannot set secrets in Deno environment');
        case 'local':
        default:
            throw ErrorFactory.createConfigError('Cannot set secrets in local environment');
    }
    // Invalidate cache
    cache.delete(key);
}
/**
 * Delete secret
 */
async function runDeleteSecret(config, cache, key) {
    switch (config.platform) {
        case 'aws':
            await deleteFromAWSSecretsManager(key);
            break;
        case 'cloudflare':
            await deleteFromCloudflareKV(config, key);
            break;
        case 'deno':
        case 'local':
        default:
            throw ErrorFactory.createConfigError('Cannot delete secrets in this environment');
    }
    // Invalidate cache
    cache.delete(key);
}
/**
 * SecretsManager implementation
 * Refactored to Functional Object pattern
 */
const SecretsManagerImpl = {
    /**
     * Create a new secrets manager instance
     */
    create(config) {
        const cache = new Map();
        return {
            /**
             * Get secret value from appropriate backend
             */
            async getSecret(key, options) {
                return runGetSecret(config, cache, key, options);
            },
            /**
             * Set secret value
             */
            async setSecret(key, value, options) {
                return runSetSecret(config, cache, key, value, options);
            },
            /**
             * Delete secret
             */
            async deleteSecret(key) {
                return runDeleteSecret(config, cache, key);
            },
            /**
             * Rotate secret (trigger new secret generation)
             */
            // eslint-disable-next-line @typescript-eslint/require-await
            async rotateSecret(_key) {
                if (config.platform === 'aws') {
                    // AWS Secrets Manager supports automatic rotation
                    throw ErrorFactory.createConfigError('Secret rotation not implemented');
                }
                throw ErrorFactory.createConfigError('Secret rotation not supported on this platform');
            },
            /**
             * Get all secrets matching pattern
             */
            async listSecrets(pattern) {
                switch (config.platform) {
                    case 'aws':
                        return listFromAWSSecretsManager(pattern);
                    case 'cloudflare':
                        return listFromCloudflareKV(config, pattern);
                    case 'deno':
                    case 'local':
                    default:
                        return [];
                }
            },
            /**
             * Clear cache (useful after rotation)
             */
            clearCache(key) {
                if (key === undefined) {
                    cache.clear();
                }
                else {
                    cache.delete(key);
                }
            },
        };
    },
};
/**
 * AWS Secrets Manager integration
 */
// eslint-disable-next-line @typescript-eslint/require-await
async function getFromAWSSecretsManager(key) {
    try {
        Logger.debug(`[AWS] Getting secret: ${key}`);
        throw ErrorFactory.createConfigError('AWS SDK not available in core - use wrapper module');
    }
    catch (error) {
        throw ErrorFactory.createTryCatchError(`Failed to retrieve secret from AWS: ${error.message}`, error);
    }
}
// eslint-disable-next-line @typescript-eslint/require-await
async function setInAWSSecretsManager(key, _value, _options) {
    Logger.info(`[AWS] Setting secret: ${key}`);
    throw ErrorFactory.createConfigError('AWS SDK not available in core - use wrapper module');
}
// eslint-disable-next-line @typescript-eslint/require-await
async function deleteFromAWSSecretsManager(key) {
    Logger.info(`[AWS] Deleting secret: ${key}`);
    throw ErrorFactory.createConfigError('AWS SDK not available in core - use wrapper module');
}
// eslint-disable-next-line @typescript-eslint/require-await
async function listFromAWSSecretsManager(pattern) {
    Logger.info(`[AWS] Listing secrets with pattern: ${pattern ?? '*'}`);
    return [];
}
/**
 * Cloudflare KV integration
 */
async function getFromCloudflareKV(config, key) {
    if (config.kv === undefined) {
        throw ErrorFactory.createConfigError('Cloudflare KV namespace not configured');
    }
    const value = await config.kv.get(key);
    if (value === null || value === '') {
        throw ErrorFactory.createNotFoundError(`Secret not found: ${key}`, { key });
    }
    return value;
}
async function setInCloudflareKV(config, key, value, options) {
    if (config.kv === undefined) {
        throw ErrorFactory.createConfigError('Cloudflare KV namespace not configured');
    }
    const ttl = options?.expirationTtl;
    await config.kv.put(key, value, { expirationTtl: ttl });
}
async function deleteFromCloudflareKV(config, key) {
    if (config.kv === undefined) {
        throw ErrorFactory.createConfigError('Cloudflare KV namespace not configured');
    }
    await config.kv.delete(key);
}
async function listFromCloudflareKV(config, pattern) {
    if (config.kv === undefined) {
        throw ErrorFactory.createConfigError('Cloudflare KV namespace not configured');
    }
    const result = await config.kv.list({ prefix: pattern });
    return result.keys.map((k) => k.name);
}
/**
 * Deno environment integration
 */
// eslint-disable-next-line @typescript-eslint/require-await
async function getFromDenoEnv(key) {
    const value = globalThis['Deno']?.env?.get?.(key);
    if (value === undefined || value === null || value === '') {
        throw ErrorFactory.createNotFoundError(`Secret not found: ${key}`, { key });
    }
    return value;
}
/**
 * Local environment variables (Node.js)
 */
// eslint-disable-next-line @typescript-eslint/require-await
async function getFromEnv(key) {
    const value = Env.get(key, '');
    if (value === undefined || value === null || value === '') {
        throw ErrorFactory.createNotFoundError(`Secret not found: ${key}`, { key });
    }
    return value;
}
/**
 * SecretsManager - Unified interface for retrieving secrets
 * Sealed namespace for immutability
 */
export const SecretsManager = Object.freeze({
    /**
     * Get or create singleton instance
     */
    getInstance(config) {
        if (instance === undefined && config !== undefined) {
            instance = SecretsManagerImpl.create(config);
        }
        if (instance === undefined) {
            throw ErrorFactory.createConfigError('SecretsManager not initialized. Call getInstance(config) first.');
        }
        return instance;
    },
    /**
     * Get secret value from appropriate backend
     */
    async getSecret(key, options) {
        return this.getInstance().getSecret(key, options);
    },
    /**
     * Set secret value
     */
    async setSecret(key, value, options) {
        return this.getInstance().setSecret(key, value, options);
    },
    /**
     * Delete secret
     */
    async deleteSecret(key) {
        return this.getInstance().deleteSecret(key);
    },
    /**
     * Rotate secret (trigger new secret generation)
     */
    async rotateSecret(key) {
        return this.getInstance().rotateSecret(key);
    },
    /**
     * Get all secrets matching pattern
     */
    async listSecrets(pattern) {
        return this.getInstance().listSecrets(pattern);
    },
    /**
     * Clear cache (useful after rotation)
     */
    clearCache(key) {
        this.getInstance().clearCache(key);
    },
});
/**
 * Predefined secret keys
 * Sealed namespace for immutability
 */
export const SECRETS = Object.freeze({
    // Database credentials
    DB_USERNAME: 'db/username',
    // Secret identifier only (not a credential value)
    DB_PASSWORD: 'db/password', // NOSONAR (typescript:S2068) - secret key name, not hardcoded password
    DB_HOST: 'db/host',
    DB_PORT: 'db/port',
    DB_DATABASE: 'db/database',
    // API keys
    JWT_SECRET: 'jwt/secret',
    JWT_REFRESH_SECRET: 'jwt/refresh-secret',
    // Encryption
    ENCRYPTION_KEY: 'encryption/key',
    ENCRYPTION_IV: 'encryption/iv',
    // Third-party APIs
    STRIPE_API_KEY: 'stripe/api-key',
    SENDGRID_API_KEY: 'sendgrid/api-key',
    GITHUB_TOKEN: 'github/token',
    // Session/CSRF
    SESSION_SECRET: 'session/secret',
    CSRF_SECRET: 'csrf/secret',
});
/**
 * Helper to get database credentials using secrets manager
 */
export async function getDatabaseCredentials() {
    const manager = SecretsManager.getInstance();
    return {
        username: await manager.getSecret(SECRETS.DB_USERNAME),
        password: await manager.getSecret(SECRETS.DB_PASSWORD),
        host: await manager.getSecret(SECRETS.DB_HOST),
        port: Number.parseInt(await manager.getSecret(SECRETS.DB_PORT), 10),
        database: await manager.getSecret(SECRETS.DB_DATABASE),
    };
}
/**
 * Helper to get JWT secrets
 */
export async function getJwtSecrets() {
    const manager = SecretsManager.getInstance();
    return {
        secret: await manager.getSecret(SECRETS.JWT_SECRET),
        refreshSecret: await manager.getSecret(SECRETS.JWT_REFRESH_SECRET),
    };
}
