"use strict";
/**
 * normalizers.ts
 *
 * Input normalization and validation helpers extracted from
 * unified-config-loader.ts (Phase 2 split — issue #1164).
 *
 * Contains: browser config normalizers, session affinity TTL normalizer,
 * composite variant validator, continuity config normalizer, and official
 * channels config normalizer.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.normalizeOfficialChannelsConfig = exports.normalizeContinuityConfig = exports.normalizeContinuityInheritanceMap = exports.validateCompositeVariants = exports.hasPositiveDuration = exports.normalizeSessionAffinityTtl = exports.canonicalizeBrowserConfig = exports.normalizeBrowserEvalMode = exports.normalizeBrowserPolicy = exports.normalizeBrowserDevtoolsPort = void 0;
const unified_config_types_1 = require("../unified-config-types");
const composite_validator_1 = require("../../cliproxy/config/composite-validator");
const official_channels_ids_1 = require("../../channels/official-channels-ids");
const browser_settings_1 = require("../../utils/browser/browser-settings");
const io_locks_1 = require("./io-locks");
// ---------------------------------------------------------------------------
// Browser normalizers
// ---------------------------------------------------------------------------
function normalizeBrowserDevtoolsPort(value) {
    if (!Number.isFinite(value)) {
        return unified_config_types_1.DEFAULT_BROWSER_CONFIG.claude.devtools_port;
    }
    const port = Math.floor(value);
    if (port < 1 || port > 65535) {
        return unified_config_types_1.DEFAULT_BROWSER_CONFIG.claude.devtools_port;
    }
    return port;
}
exports.normalizeBrowserDevtoolsPort = normalizeBrowserDevtoolsPort;
function normalizeBrowserPolicy(value) {
    return value === 'auto' || value === 'manual' ? value : unified_config_types_1.DEFAULT_BROWSER_CONFIG.claude.policy;
}
exports.normalizeBrowserPolicy = normalizeBrowserPolicy;
function normalizeBrowserEvalMode(value) {
    if (value === 'disabled' || value === 'readonly' || value === 'readwrite') {
        return value;
    }
    return unified_config_types_1.DEFAULT_BROWSER_CONFIG.claude.eval_mode ?? 'readonly';
}
exports.normalizeBrowserEvalMode = normalizeBrowserEvalMode;
function canonicalizeBrowserConfig(config, fallback = unified_config_types_1.DEFAULT_BROWSER_CONFIG) {
    const claudeUserDataDir = config?.claude?.user_data_dir === undefined
        ? fallback.claude.user_data_dir || (0, browser_settings_1.getRecommendedBrowserUserDataDir)()
        : config.claude.user_data_dir.trim() || (0, browser_settings_1.getRecommendedBrowserUserDataDir)();
    return {
        claude: {
            enabled: config?.claude?.enabled ?? fallback.claude.enabled,
            policy: normalizeBrowserPolicy(config?.claude?.policy ?? fallback.claude.policy),
            user_data_dir: claudeUserDataDir,
            devtools_port: normalizeBrowserDevtoolsPort(config?.claude?.devtools_port ?? fallback.claude.devtools_port),
            eval_mode: normalizeBrowserEvalMode(config?.claude?.eval_mode ?? fallback.claude.eval_mode),
        },
        codex: {
            enabled: config?.codex?.enabled ?? fallback.codex.enabled,
            policy: normalizeBrowserPolicy(config?.codex?.policy ?? fallback.codex.policy),
            eval_mode: normalizeBrowserEvalMode(config?.codex?.eval_mode ?? fallback.codex.eval_mode),
        },
    };
}
exports.canonicalizeBrowserConfig = canonicalizeBrowserConfig;
// ---------------------------------------------------------------------------
// Session affinity TTL normalizer
// ---------------------------------------------------------------------------
function normalizeSessionAffinityTtl(value, fallback) {
    if (typeof value !== 'string') {
        return fallback;
    }
    const trimmed = value.trim();
    if (!trimmed || !io_locks_1.GO_DURATION_PATTERN.test(trimmed) || !hasPositiveDuration(trimmed)) {
        return fallback;
    }
    return trimmed;
}
exports.normalizeSessionAffinityTtl = normalizeSessionAffinityTtl;
function hasPositiveDuration(value) {
    const segments = value.match(new RegExp(io_locks_1.GO_DURATION_SEGMENT, 'g'));
    if (!segments) {
        return false;
    }
    return segments.some((segment) => {
        const numeric = parseFloat(segment);
        return Number.isFinite(numeric) && numeric > 0;
    });
}
exports.hasPositiveDuration = hasPositiveDuration;
// ---------------------------------------------------------------------------
// Composite variant validator
// ---------------------------------------------------------------------------
/**
 * Validate composite variant provider strings.
 * Warns about invalid providers in composite variant configurations.
 */
function validateCompositeVariants(config) {
    const variants = config.cliproxy?.variants;
    if (!variants)
        return;
    for (const [name, variant] of Object.entries(variants)) {
        if ('type' in variant && variant.type === 'composite') {
            const error = (0, composite_validator_1.validateCompositeTiers)(variant.tiers, {
                defaultTier: variant.default_tier,
                requireAllTiers: true,
            });
            if (error) {
                console.warn(`[!] Variant '${name}': invalid composite config (${error})`);
            }
        }
    }
}
exports.validateCompositeVariants = validateCompositeVariants;
// ---------------------------------------------------------------------------
// Continuity config normalizer
// ---------------------------------------------------------------------------
/**
 * Normalize continuity inheritance mapping payload.
 * Keeps only non-empty string keys and values.
 */
function normalizeContinuityInheritanceMap(value) {
    if (typeof value !== 'object' || value === null || Array.isArray(value)) {
        return undefined;
    }
    const normalized = {};
    for (const [profileName, accountName] of Object.entries(value)) {
        const normalizedProfile = profileName.trim();
        const normalizedAccount = typeof accountName === 'string' ? accountName.trim() : '';
        if (!normalizedProfile || !normalizedAccount) {
            continue;
        }
        normalized[normalizedProfile] = normalizedAccount;
    }
    return Object.keys(normalized).length > 0 ? normalized : undefined;
}
exports.normalizeContinuityInheritanceMap = normalizeContinuityInheritanceMap;
/**
 * Normalize continuity section.
 * Supports legacy root key: continuity_inherit_from_account.
 */
function normalizeContinuityConfig(partial) {
    const legacyMap = normalizeContinuityInheritanceMap(partial
        .continuity_inherit_from_account);
    const continuityMap = normalizeContinuityInheritanceMap(partial.continuity?.inherit_from_account);
    if (!legacyMap && !continuityMap) {
        return undefined;
    }
    return {
        inherit_from_account: {
            ...(legacyMap ?? {}),
            ...(continuityMap ?? {}),
        },
    };
}
exports.normalizeContinuityConfig = normalizeContinuityConfig;
function normalizeOfficialChannelsConfig(partial) {
    const hasCanonicalChannelsSection = partial.channels !== undefined;
    const hasExplicitSelectedField = hasCanonicalChannelsSection &&
        Object.prototype.hasOwnProperty.call(partial.channels, 'selected');
    const rawSelected = hasExplicitSelectedField && Array.isArray(partial.channels?.selected)
        ? partial.channels.selected.filter((value) => (0, official_channels_ids_1.isOfficialChannelId)(value))
        : [];
    return {
        selected: hasCanonicalChannelsSection
            ? (0, official_channels_ids_1.normalizeOfficialChannelIds)(rawSelected)
            : (0, official_channels_ids_1.resolveLegacyDiscordSelection)(partial.discord_channels?.enabled),
        unattended: partial.channels?.unattended ??
            partial.discord_channels?.unattended ??
            unified_config_types_1.DEFAULT_OFFICIAL_CHANNELS_CONFIG.unattended,
    };
}
exports.normalizeOfficialChannelsConfig = normalizeOfficialChannelsConfig;
//# sourceMappingURL=normalizers.js.map