/**
 * Notification Configuration
 *
 * Config-first mapping of notification providers/channels.
 * 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 normalizeName = (value) => value.trim().toLowerCase();
const hasOwn = (obj, key) => {
    return Object.hasOwn(obj, key);
};
const getDefaultChannel = (drivers) => {
    const envSelectedRaw = Env.get('NOTIFICATION_CONNECTION', Env.get('NOTIFICATION_DRIVER', 'console'));
    const value = normalizeName(envSelectedRaw ?? 'console');
    if (value.length > 0 && hasOwn(drivers, value))
        return value;
    if (envSelectedRaw.trim().length > 0) {
        throw ErrorFactory.createConfigError(`Notification channel not configured: ${value}`);
    }
    return hasOwn(drivers, 'console') ? 'console' : Object.keys(drivers)[0] ?? 'console';
};
const getNotificationDriver = (config, name) => {
    const selected = normalizeName(String(name ?? config.default));
    const channelName = selected === 'default' ? normalizeName(config.default) : selected;
    const isExplicitSelection = name !== undefined &&
        String(name).trim().length > 0 &&
        normalizeName(String(name)) !== 'default';
    if (channelName.length > 0 && hasOwn(config.drivers, channelName)) {
        const resolved = config.drivers[channelName];
        if (resolved !== undefined)
            return resolved;
    }
    if (Object.keys(config.drivers ?? {}).length === 0) {
        throw ErrorFactory.createConfigError('No notification channels are configured');
    }
    if (isExplicitSelection) {
        throw ErrorFactory.createConfigError(`Notification channel not configured: ${channelName}`);
    }
    // Default selection is strict: if `default` points at an unconfigured channel, throw.
    throw ErrorFactory.createConfigError(`Notification channel not configured: ${channelName}`);
};
const getBaseProviders = () => {
    return {
        console: { driver: 'console' },
        termii: {
            driver: 'termii',
            apiKey: Env.get('TERMII_API_KEY', ''),
            sender: Env.get('TERMII_SENDER', 'ZinTrust'),
            endpoint: Env.get('TERMII_ENDPOINT', 'https://api.termii.com/sms/send'),
        },
        twilio: {
            driver: 'twilio',
            accountSid: Env.get('TWILIO_ACCOUNT_SID', ''),
            authToken: Env.get('TWILIO_AUTH_TOKEN', ''),
            fromNumber: Env.get('TWILIO_FROM_NUMBER', ''),
        },
        slack: {
            driver: 'slack',
            webhookUrl: Env.get('SLACK_WEBHOOK_URL', ''),
        },
    };
};
const createNotificationConfig = () => {
    const overrides = StartupConfigFileRegistry.get(StartupConfigFile.Notification) ??
        {};
    const notificationConfigObj = {
        /**
         * Default notification channel name (normalized).
         */
        get default() {
            const overrideDefault = overrides.default;
            if (typeof overrideDefault === 'string' && overrideDefault.trim().length > 0) {
                const value = normalizeName(overrideDefault);
                if (value.length > 0 && hasOwn(this.drivers, value))
                    return value;
                throw ErrorFactory.createConfigError(`Notification channel not configured: ${value}`);
            }
            return getDefaultChannel(this.drivers);
        },
        /**
         * Notification channels.
         *
         * You may add custom named channels (e.g. `opsSlack`, `smsMarketing`) that
         * point to any known driver config.
         */
        get drivers() {
            // Return a record of channels; can be extended by app-level config.
            return {
                ...getBaseProviders(),
                ...overrides.providers,
                ...overrides.drivers,
            };
        },
        /**
         * Legacy provider configs (kept for backwards compatibility with wrappers).
         */
        get providers() {
            return {
                ...getBaseProviders(),
                ...overrides.providers,
            };
        },
        /**
         * Normalized notification channel name.
         */
        getDriverName() {
            return normalizeName(this.default);
        },
        /**
         * Resolve a channel config.
         * - Unknown names throw when explicitly selected.
         * - `default` is a reserved alias of the configured default.
         */
        getDriverConfig(name) {
            return getNotificationDriver(this, name);
        },
    };
    return Object.freeze(notificationConfigObj);
};
let cached = null;
const proxyTarget = {};
const ensureNotificationConfig = () => {
    if (cached)
        return cached;
    cached = createNotificationConfig();
    try {
        Object.defineProperties(proxyTarget, Object.getOwnPropertyDescriptors(cached));
    }
    catch {
        // best-effort
    }
    return cached;
};
export const notificationConfig = new Proxy(proxyTarget, {
    get(_target, prop) {
        return ensureNotificationConfig()[prop];
    },
    ownKeys() {
        ensureNotificationConfig();
        return Reflect.ownKeys(proxyTarget);
    },
    getOwnPropertyDescriptor(_target, prop) {
        ensureNotificationConfig();
        return Object.getOwnPropertyDescriptor(proxyTarget, prop);
    },
});
export default notificationConfig;
