/**
 * Queue Configuration
 * Background job and message queue settings
 * Sealed namespace for immutability
 */
import { Cloudflare } from './cloudflare.js';
import { Env } from './env.js';
import { Logger } from './logger.js';
import { middlewareConfig } from './middleware.js';
import { ErrorFactory } from '../exceptions/ZintrustError.js';
import { ZintrustLang } from '../lang/lang.js';
import { StartupConfigFile, StartupConfigFileRegistry } from '../runtime/StartupConfigFileRegistry.js';
const getQueueDriver = (driverConfig) => {
    const driverName = driverConfig.default;
    return driverConfig.drivers[driverName];
};
const readWorkersEnvString = (key) => {
    const workerValue = Cloudflare.getWorkersVar(key);
    if (workerValue !== null && workerValue.trim() !== '')
        return workerValue;
    return '';
};
const readWorkersFallbackString = (workersKey, fallbackKey, fallback = '') => {
    const workerValue = readWorkersEnvString(workersKey);
    if (workerValue.trim() !== '')
        return workerValue;
    return Env.get(fallbackKey, fallback);
};
const readWorkersFallbackInt = (workersKey, fallbackKey, fallback) => {
    const raw = readWorkersFallbackString(workersKey, fallbackKey, String(fallback));
    if (raw.trim() === '')
        return fallback;
    const parsed = Number.parseInt(raw, 10);
    return Number.isFinite(parsed) ? parsed : fallback;
};
const parseRedisUrl = (rawUrl) => {
    try {
        const url = new URL(rawUrl);
        if (url.protocol !== 'redis:' && url.protocol !== 'rediss:')
            return null;
        const host = url.hostname;
        const port = url.port ? Number.parseInt(url.port, 10) : 6379;
        const password = url.password ? decodeURIComponent(url.password) : undefined;
        const db = url.pathname ? Number.parseInt(url.pathname.replace('/', ''), 10) : undefined;
        return { host, port, password, database: Number.isFinite(db ?? Number.NaN) ? db : undefined };
    }
    catch {
        return null;
    }
};
const resolveRedisProxyConfig = () => {
    const isWorkersRuntime = Cloudflare.getWorkersEnv() !== null;
    if (!isWorkersRuntime) {
        return null;
    }
    const proxyUrl = Env.get('REDIS_PROXY_URL', '').trim();
    const parsed = proxyUrl ? parseRedisUrl(proxyUrl) : null;
    if (parsed)
        return parsed;
    if (Env.getBool('USE_REDIS_PROXY', false)) {
        return {
            host: Env.get('REDIS_PROXY_HOST', ''),
            port: Env.getInt('REDIS_PROXY_PORT', 6379),
            password: Env.get('REDIS_PASSWORD', ''),
            database: Env.getInt('REDIS_QUEUE_DB', ZintrustLang.REDIS_DEFAULT_DB),
        };
    }
    return null;
};
/**
 * Helper: Create base driver configurations from environment
 */
export const createBaseDrivers = () => ({
    sync: {
        driver: 'sync',
    },
    memory: {
        driver: 'memory',
        ttl: Env.getInt('QUEUE_MEMORY_TTL', 3600000), // 1 hour default
    },
    database: {
        driver: 'database',
        table: Env.get('QUEUE_TABLE', 'jobs'),
        connection: Env.get('QUEUE_DB_CONNECTION', 'default'),
    },
    redis: {
        driver: 'redis',
        host: resolveRedisProxyConfig()?.host ??
            readWorkersFallbackString('WORKERS_REDIS_HOST', 'REDIS_HOST', 'localhost'),
        port: resolveRedisProxyConfig()?.port ??
            readWorkersFallbackInt('WORKERS_REDIS_PORT', 'REDIS_PORT', 6379),
        password: resolveRedisProxyConfig()?.password ??
            readWorkersFallbackString('WORKERS_REDIS_PASSWORD', 'REDIS_PASSWORD'),
        database: resolveRedisProxyConfig()?.database ??
            readWorkersFallbackInt('WORKERS_REDIS_QUEUE_DB', 'REDIS_QUEUE_DB', ZintrustLang.REDIS_DEFAULT_DB),
    },
    rabbitmq: {
        driver: 'rabbitmq',
        host: readWorkersFallbackString('WORKERS_RABBITMQ_HOST', 'RABBITMQ_HOST', 'localhost'),
        port: readWorkersFallbackInt('WORKERS_RABBITMQ_PORT', 'RABBITMQ_PORT', 5672),
        username: readWorkersFallbackString('WORKERS_RABBITMQ_USER', 'RABBITMQ_USER', 'guest'),
        password: readWorkersFallbackString('WORKERS_RABBITMQ_PASSWORD', 'RABBITMQ_PASSWORD', 'guest'),
        vhost: readWorkersFallbackString('WORKERS_RABBITMQ_VHOST', 'RABBITMQ_VHOST', '/'),
        httpGatewayUrl: readWorkersFallbackString('WORKERS_RABBITMQ_HTTP_GATEWAY_URL', 'RABBITMQ_HTTP_GATEWAY_URL'),
        httpGatewayToken: readWorkersFallbackString('WORKERS_RABBITMQ_HTTP_GATEWAY_TOKEN', 'RABBITMQ_HTTP_GATEWAY_TOKEN'),
        httpGatewayTimeoutMs: readWorkersFallbackInt('WORKERS_RABBITMQ_HTTP_GATEWAY_TIMEOUT_MS', 'RABBITMQ_HTTP_GATEWAY_TIMEOUT_MS', 15000),
    },
    sqs: {
        driver: 'sqs',
        key: Env.get('AWS_ACCESS_KEY_ID'),
        secret: Env.get('AWS_SECRET_ACCESS_KEY'),
        region: Env.AWS_REGION,
        queueUrl: Env.get('AWS_SQS_QUEUE_URL'),
    },
});
/**
 * Helper: Create monitor configuration from environment
 */
const createBaseMonitor = () => {
    const enabled = Env.getBool('QUEUE_MONITOR_ENABLED', false);
    const basePath = Env.get('QUEUE_MONITOR_BASE_PATH', '/queue-monitor');
    const middleware = Env.get('QUEUE_MONITOR_MIDDLEWARE', '')
        .split(',')
        .map((m) => m.trim())
        .filter((m) => m.length > 0);
    if (enabled && middleware.length > 0) {
        const knownKeys = new Set(Object.keys(middlewareConfig.route ?? {}));
        const unknownKeys = middleware.filter((name) => !knownKeys.has(name));
        if (unknownKeys.length > 0) {
            Logger.error('Unknown QUEUE_MONITOR_MIDDLEWARE keys configured', {
                unknownKeys,
                basePath,
            });
            throw ErrorFactory.createConfigError(`Unknown QUEUE_MONITOR_MIDDLEWARE key(s): ${unknownKeys.join(', ')}. These must match registered route middleware keys in your app.`);
        }
    }
    return {
        enabled,
        basePath,
        middleware,
        autoRefresh: Env.getBool('QUEUE_MONITOR_AUTO_REFRESH', true),
        refreshIntervalMs: Env.getInt('QUEUE_MONITOR_REFRESH_MS', 5000),
    };
};
const createQueueConfig = () => {
    const overrides = StartupConfigFileRegistry.get(StartupConfigFile.Queue) ?? {};
    const baseDefault = Env.get('QUEUE_DRIVER', 'sync');
    const baseDrivers = createBaseDrivers();
    const baseFailed = {
        database: Env.get('FAILED_JOBS_DB_CONNECTION', 'default'),
        table: Env.get('FAILED_JOBS_TABLE', 'failed_jobs'),
    };
    const baseProcessing = {
        timeout: Env.getInt('QUEUE_JOB_TIMEOUT', 60),
        retries: Env.getInt('QUEUE_JOB_RETRIES', 3),
        backoff: Env.getInt('QUEUE_JOB_BACKOFF', 0),
        workers: Env.getInt('QUEUE_WORKERS', 1),
    };
    const baseMonitor = createBaseMonitor();
    const mergedDrivers = {
        ...baseDrivers,
        ...overrides.drivers,
    };
    const queueConfigObj = {
        /**
         * Default queue driver
         */
        default: overrides.default ?? baseDefault,
        /**
         * Queue drivers
         */
        drivers: mergedDrivers,
        /**
         * Get queue driver config
         */
        getDriver(driverConfig) {
            return getQueueDriver(driverConfig);
        },
        /**
         * Failed jobs table
         */
        failed: {
            ...baseFailed,
            ...overrides.failed,
        },
        /**
         * Job processing
         */
        processing: {
            ...baseProcessing,
            ...overrides.processing,
        },
        /**
         * Queue Monitor settings
         */
        monitor: {
            ...baseMonitor,
            ...overrides.monitor,
        },
    };
    return Object.freeze(queueConfigObj);
};
let cached = null;
const proxyTarget = {};
const ensureQueueConfig = () => {
    if (cached)
        return cached;
    cached = createQueueConfig();
    try {
        Object.defineProperties(proxyTarget, Object.getOwnPropertyDescriptors(cached));
    }
    catch {
        // best-effort
    }
    return cached;
};
export const queueConfig = new Proxy(proxyTarget, {
    get(_target, prop) {
        return ensureQueueConfig()[prop];
    },
    ownKeys() {
        ensureQueueConfig();
        return Reflect.ownKeys(proxyTarget);
    },
    getOwnPropertyDescriptor(_target, prop) {
        ensureQueueConfig();
        return Object.getOwnPropertyDescriptor(proxyTarget, prop);
    },
});
