"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.reconcileCodexModelForActivePlan = exports.resolveRuntimeCodexFallbackModel = exports.parseCodexUnsupportedModelError = exports.getFreePlanFallbackCodexModel = exports.getDefaultCodexModel = exports.normalizeCodexModelId = void 0;
const account_manager_1 = require("../accounts/account-manager");
const model_catalog_1 = require("../model-catalog");
const model_id_normalizer_1 = require("./model-id-normalizer");
const quota_fetcher_codex_1 = require("../quota/quota-fetcher-codex");
const quota_response_cache_1 = require("../quota/quota-response-cache");
const ui_1 = require("../../utils/ui");
const FREE_SAFE_DEFAULT_MODEL = 'gpt-5.4';
const FREE_SAFE_FAST_MODEL = 'gpt-5.4-mini';
const CODEX_TUNING_SUFFIX_REGEX = /(?:-(?:minimal|low|medium|high|xhigh)(?:-fast)?|-fast(?:-(?:minimal|low|medium|high|xhigh))?)$/i;
const CODEX_PAREN_SUFFIX_REGEX = /\((minimal|low|medium|high|xhigh)\)$/i;
const EXTENDED_CONTEXT_SUFFIX_REGEX = /\[1m\]$/i;
const KNOWN_CODEX_MODELS = new Set(((0, model_catalog_1.getProviderCatalog)('codex')?.models ?? []).map((model) => model.id.toLowerCase()));
const FREE_PLAN_FALLBACKS = new Map([
    ['gpt-5.5', FREE_SAFE_DEFAULT_MODEL],
    ['gpt-5.3-codex', FREE_SAFE_DEFAULT_MODEL],
    ['gpt-5.3-codex-spark', FREE_SAFE_FAST_MODEL],
]);
function isRecord(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function isKnownCodexModel(model) {
    return KNOWN_CODEX_MODELS.has(model);
}
function normalizeCodexModelId(model) {
    const stripped = model
        .trim()
        .replace(EXTENDED_CONTEXT_SUFFIX_REGEX, '')
        .replace(CODEX_PAREN_SUFFIX_REGEX, '')
        .replace(CODEX_TUNING_SUFFIX_REGEX, '')
        .trim();
    return (0, model_id_normalizer_1.normalizeModelIdForProvider)(stripped, 'codex').trim().toLowerCase();
}
exports.normalizeCodexModelId = normalizeCodexModelId;
function getDefaultCodexModel() {
    return FREE_SAFE_DEFAULT_MODEL;
}
exports.getDefaultCodexModel = getDefaultCodexModel;
function getFreePlanFallbackCodexModel(model) {
    return FREE_PLAN_FALLBACKS.get(normalizeCodexModelId(model)) ?? null;
}
exports.getFreePlanFallbackCodexModel = getFreePlanFallbackCodexModel;
function parseCodexUnsupportedModelError(statusCode, responseBody) {
    if (statusCode !== 400 || !responseBody.trim()) {
        return null;
    }
    try {
        const parsed = JSON.parse(responseBody);
        if (!isRecord(parsed) ||
            !isRecord(parsed.error) ||
            parsed.error.code !== 'model_not_supported') {
            return null;
        }
        return {
            message: typeof parsed.error.message === 'string' ? parsed.error.message : null,
            code: 'model_not_supported',
            param: typeof parsed.error.param === 'string' ? parsed.error.param : null,
            type: typeof parsed.error.type === 'string' ? parsed.error.type : null,
        };
    }
    catch {
        return null;
    }
}
exports.parseCodexUnsupportedModelError = parseCodexUnsupportedModelError;
function resolveRuntimeCodexFallbackModel(options) {
    const requestedModel = normalizeCodexModelId(options.requestedModel);
    if (!requestedModel) {
        return null;
    }
    const excludedModels = new Set((options.excludeModels ?? []).map((model) => normalizeCodexModelId(model)).filter(Boolean));
    const candidates = [
        options.modelMap.defaultModel,
        getFreePlanFallbackCodexModel(requestedModel),
        options.modelMap.opusModel,
        options.modelMap.sonnetModel,
        options.modelMap.haikuModel,
        getDefaultCodexModel(),
    ];
    for (const candidate of candidates) {
        if (!candidate)
            continue;
        const normalizedCandidate = normalizeCodexModelId(candidate);
        if (!normalizedCandidate ||
            normalizedCandidate === requestedModel ||
            excludedModels.has(normalizedCandidate) ||
            !isKnownCodexModel(normalizedCandidate)) {
            continue;
        }
        return normalizedCandidate;
    }
    return null;
}
exports.resolveRuntimeCodexFallbackModel = resolveRuntimeCodexFallbackModel;
async function reconcileCodexModelForActivePlan(options, deps = {}) {
    const { currentModel, verbose } = options;
    if (!currentModel)
        return;
    const fallbackModel = getFreePlanFallbackCodexModel(currentModel);
    if (!fallbackModel)
        return;
    const resolveDefaultAccount = deps.getDefaultAccount ?? account_manager_1.getDefaultAccount;
    const fetchQuota = deps.fetchCodexQuota ?? quota_fetcher_codex_1.fetchCodexQuota;
    const formatInfo = deps.formatInfo ?? ui_1.info;
    const formatWarn = deps.formatWarn ?? ui_1.warn;
    const defaultAccount = resolveDefaultAccount('codex');
    if (!defaultAccount) {
        console.error(formatWarn(`Configured Codex model "${normalizeCodexModelId(currentModel)}" may require a paid Codex plan. ` +
            `If startup fails, switch to "${fallbackModel}" with "ccs codex --config".`));
        return;
    }
    const cachedQuota = (0, quota_response_cache_1.getCachedQuota)('codex', defaultAccount.id);
    const quota = cachedQuota ?? (await fetchQuota(defaultAccount.id, verbose));
    if (!cachedQuota) {
        (0, quota_response_cache_1.setCachedQuota)('codex', defaultAccount.id, quota);
    }
    if (quota.planType === 'free') {
        console.error(formatInfo(`Codex free plan detected. Keeping saved model "${normalizeCodexModelId(currentModel)}" in settings; ` +
            `runtime requests will fall back to "${fallbackModel}" when needed.`));
        return;
    }
    if (quota.planType) {
        return;
    }
    console.error(formatWarn(`Could not verify Codex plan for model "${normalizeCodexModelId(currentModel)}". ` +
        `If startup fails with model_not_supported, switch to "${fallbackModel}" via "ccs codex --config".`));
}
exports.reconcileCodexModelForActivePlan = reconcileCodexModelForActivePlan;
//# sourceMappingURL=codex-plan-compatibility.js.map