/**
 * Broadcast Configuration
 *
 * Centralizes broadcast driver selection and provider env mappings.
 * Driver selection must be dynamic (tests may mutate process.env).
 */
import { Env } from './env.js';
import { ErrorFactory } from '../exceptions/ZintrustError.js';
import { StartupConfigFile, StartupConfigFileRegistry } from '../runtime/StartupConfigFileRegistry.js';
const normalizeDriverName = (value) => value.trim().toLowerCase();
const hasOwn = (obj, key) => {
    return Object.hasOwn(obj, key);
};
const getDefaultBroadcaster = (drivers) => {
    const envSelectedRaw = Env.get('BROADCAST_CONNECTION', Env.get('BROADCAST_DRIVER', 'inmemory'));
    const value = normalizeDriverName(envSelectedRaw ?? 'inmemory');
    if (value.length > 0 && hasOwn(drivers, value))
        return value;
    if (envSelectedRaw.trim().length > 0) {
        throw ErrorFactory.createConfigError(`Broadcast driver not configured: ${value}`);
    }
    return hasOwn(drivers, 'inmemory') ? 'inmemory' : (Object.keys(drivers)[0] ?? 'inmemory');
};
const getPusherConfig = () => ({
    driver: 'pusher',
    appId: Env.get('PUSHER_APP_ID', ''),
    key: Env.get('PUSHER_APP_KEY', ''),
    secret: Env.get('PUSHER_APP_SECRET', ''),
    cluster: Env.get('PUSHER_APP_CLUSTER', ''),
    useTLS: Env.getBool('PUSHER_USE_TLS', true),
});
const getRedisConfig = () => ({
    driver: 'redis',
    host: Env.get('BROADCAST_REDIS_HOST', Env.get('REDIS_HOST', 'localhost')),
    port: Env.getInt('BROADCAST_REDIS_PORT', Env.getInt('REDIS_PORT', 6379)),
    password: Env.get('BROADCAST_REDIS_PASSWORD', Env.get('REDIS_PASSWORD', '')),
    channelPrefix: Env.get('BROADCAST_CHANNEL_PREFIX', 'broadcast:'),
});
const getRedisHttpsConfig = () => ({
    driver: 'redishttps',
    endpoint: Env.get('REDIS_HTTPS_ENDPOINT', ''),
    token: Env.get('REDIS_HTTPS_TOKEN', ''),
    channelPrefix: Env.get('BROADCAST_CHANNEL_PREFIX', 'broadcast:'),
});
const getBroadcastDriver = (config, name) => {
    const selected = normalizeDriverName(String(name ?? config.default));
    const broadcasterName = selected === 'default' ? normalizeDriverName(config.default) : selected;
    const isExplicitSelection = name !== undefined &&
        String(name).trim().length > 0 &&
        normalizeDriverName(String(name)) !== 'default';
    if (broadcasterName.length > 0 && hasOwn(config.drivers, broadcasterName)) {
        const resolved = config.drivers[broadcasterName];
        if (resolved !== undefined)
            return resolved;
    }
    if (isExplicitSelection) {
        throw ErrorFactory.createConfigError(`Broadcast driver not configured: ${broadcasterName}`);
    }
    const fallback = config.drivers['inmemory'] ?? Object.values(config.drivers)[0];
    if (fallback !== undefined)
        return fallback;
    throw ErrorFactory.createConfigError('No broadcast drivers are configured');
};
const createBroadcastConfig = () => {
    const overrides = StartupConfigFileRegistry.get(StartupConfigFile.Broadcast) ?? {};
    const broadcastConfigObj = {
        /**
         * Default broadcaster name (normalized).
         */
        get default() {
            const overrideDefault = overrides.default;
            if (typeof overrideDefault === 'string' && overrideDefault.trim().length > 0) {
                const value = normalizeDriverName(overrideDefault);
                if (value.length > 0 && hasOwn(this.drivers, value))
                    return value;
                throw ErrorFactory.createConfigError(`Broadcast driver not configured: ${value}`);
            }
            return getDefaultBroadcaster(this.drivers);
        },
        /**
         * Broadcast drivers.
         *
         * You may add custom named broadcasters (e.g. `ops`, `billing`) that point to any
         * known driver config.
         */
        get drivers() {
            return {
                inmemory: { driver: 'inmemory' },
                pusher: getPusherConfig(),
                redis: getRedisConfig(),
                redishttps: getRedisHttpsConfig(),
                ...overrides.drivers,
            };
        },
        /**
         * Normalized broadcast driver name for the default broadcaster.
         */
        getDriverName() {
            return normalizeDriverName(this.default);
        },
        /**
         * Get a config object for the currently selected driver.
         * Defaults to inmemory for unknown/unsupported names.
         */
        getDriverConfig(name) {
            return getBroadcastDriver(this, name);
        },
    };
    return Object.freeze(broadcastConfigObj);
};
let cached = null;
const proxyTarget = {};
const ensureBroadcastConfig = () => {
    if (cached)
        return cached;
    cached = createBroadcastConfig();
    try {
        Object.defineProperties(proxyTarget, Object.getOwnPropertyDescriptors(cached));
    }
    catch {
        // best-effort
    }
    return cached;
};
const broadcastConfig = new Proxy(proxyTarget, {
    get(_target, prop) {
        return ensureBroadcastConfig()[prop];
    },
    ownKeys() {
        ensureBroadcastConfig();
        return Reflect.ownKeys(proxyTarget);
    },
    getOwnPropertyDescriptor(_target, prop) {
        ensureBroadcastConfig();
        return Object.getOwnPropertyDescriptor(proxyTarget, prop);
    },
});
export default broadcastConfig;
