"use strict";
/**
 * Settings Routes - Settings and preset management
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const lockfile = __importStar(require("proper-lockfile"));
const sensitive_keys_1 = require("../../utils/sensitive-keys");
const variant_service_1 = require("../../cliproxy/services/variant-service");
const cliproxy_1 = require("../../cliproxy");
const config_generator_1 = require("../../cliproxy/config/config-generator");
const hook_utils_1 = require("../../utils/websearch/hook-utils");
const image_analyzer_hook_utils_1 = require("../../utils/hooks/image-analyzer-hook-utils");
const services_1 = require("../../api/services");
const auth_middleware_1 = require("../middleware/auth-middleware");
const provider_capabilities_1 = require("../../cliproxy/provider-capabilities");
const env_builder_1 = require("../../cliproxy/config/env-builder");
const helpers_1 = require("../../utils/helpers");
const model_id_normalizer_1 = require("../../cliproxy/ai-providers/model-id-normalizer");
const route_helpers_1 = require("./route-helpers");
const image_analyzer_profile_hook_injector_1 = require("../../utils/hooks/image-analyzer-profile-hook-injector");
const image_analyzer_hook_installer_1 = require("../../utils/hooks/image-analyzer-hook-installer");
const hooks_1 = require("../../utils/hooks");
const config_loader_facade_1 = require("../../config/config-loader-facade");
const router = (0, express_1.Router)();
const MODEL_ENV_KEYS = [
    'ANTHROPIC_MODEL',
    'ANTHROPIC_DEFAULT_OPUS_MODEL',
    'ANTHROPIC_DEFAULT_SONNET_MODEL',
    'ANTHROPIC_DEFAULT_HAIKU_MODEL',
];
const PRESET_MODEL_KEYS = ['default', 'opus', 'sonnet', 'haiku'];
const SETTINGS_IDENTIFIER_PATTERN = /^[a-zA-Z][a-zA-Z0-9._-]*$/;
const { logRouteError, respondInternalError } = (0, route_helpers_1.createRouteErrorHelpers)('settings-routes');
function resolvePathWithin(basePath, targetPath) {
    const resolvedBase = path.resolve(basePath);
    const resolvedTarget = path.resolve(path.isAbsolute(targetPath) ? targetPath : path.join(resolvedBase, targetPath));
    if (!isPathWithin(resolvedBase, resolvedTarget)) {
        throw new Error('Invalid settings path');
    }
    return resolvedTarget;
}
function normalizePathForComparison(targetPath) {
    const resolvedPath = path.resolve(targetPath);
    return process.platform === 'win32' ? resolvedPath.toLowerCase() : resolvedPath;
}
function isPathWithin(basePath, targetPath) {
    const normalizedBase = normalizePathForComparison(basePath);
    const normalizedTarget = normalizePathForComparison(targetPath);
    const relative = path.relative(normalizedBase, normalizedTarget);
    return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
}
function safeRealPath(targetPath) {
    try {
        return fs.realpathSync.native(targetPath);
    }
    catch {
        return null;
    }
}
function getRealCcsDir() {
    return safeRealPath((0, config_loader_facade_1.getCcsDir)()) ?? path.resolve((0, config_loader_facade_1.getCcsDir)());
}
function requirePathWithinRealCcsDir(targetPath) {
    if (!isPathWithin(getRealCcsDir(), targetPath)) {
        throw new Error('Invalid settings path');
    }
}
function requireExistingSettingsPathWithinCcs(settingsPath) {
    const realSettingsPath = safeRealPath(settingsPath);
    if (!realSettingsPath) {
        return false;
    }
    requirePathWithinRealCcsDir(realSettingsPath);
    return true;
}
function findExistingParentRealPath(targetPath) {
    let currentPath = path.dirname(targetPath);
    while (true) {
        const realParentPath = safeRealPath(currentPath);
        if (realParentPath) {
            return realParentPath;
        }
        const parentPath = path.dirname(currentPath);
        if (parentPath === currentPath) {
            return null;
        }
        currentPath = parentPath;
    }
}
function requireWritableSettingsPathWithinCcs(settingsPath) {
    const realSettingsPath = safeRealPath(settingsPath);
    if (realSettingsPath) {
        requirePathWithinRealCcsDir(realSettingsPath);
        return;
    }
    const realParentPath = findExistingParentRealPath(settingsPath);
    if (!realParentPath) {
        throw new Error('Invalid settings path');
    }
    requirePathWithinRealCcsDir(realParentPath);
}
function resolveSettingsCandidatePath(ccsDir, settingsPath) {
    return resolvePathWithin(ccsDir, (0, helpers_1.expandPath)(settingsPath));
}
function requireSensitiveLocalAccess(req, res) {
    return (0, auth_middleware_1.requireLocalAccessWhenAuthDisabled)(req, res, 'Sensitive settings endpoints require localhost access when dashboard auth is disabled.');
}
function classifyConfigSaveFailure(error) {
    const message = error instanceof Error ? error.message.toLowerCase() : '';
    if (message.includes('failed to acquire config lock')) {
        return { statusCode: 409, message: 'Configuration is busy. Retry in a moment.' };
    }
    if (message.includes('eacces') || message.includes('eperm') || message.includes('permission')) {
        return { statusCode: 403, message: 'Insufficient permission to update configuration.' };
    }
    if (message.includes('enospc') || message.includes('no space left')) {
        return { statusCode: 507, message: 'Insufficient disk space to update configuration.' };
    }
    return { statusCode: 500, message: 'Failed to update Antigravity power user mode.' };
}
/**
 * Helper: Resolve settings path for profile or variant
 * Variants have settings paths in config, regular profiles use {name}.settings.json
 */
function resolveSettingsPath(profileOrVariant) {
    if (!SETTINGS_IDENTIFIER_PATTERN.test(profileOrVariant)) {
        throw new Error('Invalid profile name');
    }
    const ccsDir = (0, config_loader_facade_1.getCcsDir)();
    const resolvedCcsDir = path.resolve(ccsDir);
    const directProvider = (0, provider_capabilities_1.mapExternalProviderName)(profileOrVariant);
    if (directProvider) {
        if (profileOrVariant !== directProvider) {
            return resolvePathWithin(resolvedCcsDir, path.join(resolvedCcsDir, `${profileOrVariant}.settings.json`));
        }
        return resolvePathWithin(resolvedCcsDir, (0, env_builder_1.resolveProviderSettingsPath)(directProvider));
    }
    // Check if this is a variant
    const variants = (0, variant_service_1.listVariants)();
    const variant = variants[profileOrVariant];
    if (variant?.settings) {
        return resolveSettingsCandidatePath(resolvedCcsDir, variant.settings);
    }
    let configuredSettingsPath;
    try {
        configuredSettingsPath = (0, config_loader_facade_1.loadConfigSafe)().profiles[profileOrVariant];
    }
    catch {
        // Fall back to the conventional ~/.ccs/<profile>.settings.json path below.
    }
    if (typeof configuredSettingsPath === 'string' && configuredSettingsPath.trim().length > 0) {
        return resolveSettingsCandidatePath(resolvedCcsDir, configuredSettingsPath);
    }
    // Regular profile settings
    return resolvePathWithin(resolvedCcsDir, path.join(resolvedCcsDir, `${profileOrVariant}.settings.json`));
}
function resolveProviderForProfile(profileOrVariant) {
    const directProvider = (0, provider_capabilities_1.mapExternalProviderName)(profileOrVariant);
    if (directProvider) {
        return directProvider;
    }
    const variants = (0, variant_service_1.listVariants)();
    const variantProvider = variants[profileOrVariant]?.provider;
    if (typeof variantProvider === 'string') {
        return (0, provider_capabilities_1.mapExternalProviderName)(variantProvider);
    }
    return null;
}
function resolveProviderFromBaseUrl(baseUrl) {
    if (typeof baseUrl !== 'string' || baseUrl.trim().length === 0) {
        return null;
    }
    try {
        const parsed = new URL(baseUrl);
        const extracted = (0, model_id_normalizer_1.extractProviderFromPathname)(parsed.pathname);
        return extracted ? (0, provider_capabilities_1.mapExternalProviderName)(extracted) : null;
    }
    catch {
        const extracted = (0, model_id_normalizer_1.extractProviderFromPathname)(baseUrl);
        return extracted ? (0, provider_capabilities_1.mapExternalProviderName)(extracted) : null;
    }
}
function resolveProviderForSettings(profileOrVariant, settings) {
    const providerFromProfile = resolveProviderForProfile(profileOrVariant);
    if (providerFromProfile) {
        return providerFromProfile;
    }
    const baseUrl = settings?.env && typeof settings.env === 'object' ? settings.env.ANTHROPIC_BASE_URL : undefined;
    return resolveProviderFromBaseUrl(baseUrl);
}
function canonicalizeProfileModelId(profileOrVariant, modelId, settings) {
    const provider = resolveProviderForSettings(profileOrVariant, settings);
    if (!provider)
        return modelId;
    return (0, model_id_normalizer_1.canonicalizeModelIdForProvider)(modelId, provider);
}
function findDeniedProfileModel(profileOrVariant, modelId, settings) {
    const provider = resolveProviderForSettings(profileOrVariant, settings);
    if (!provider)
        return null;
    return (0, model_id_normalizer_1.getDeniedModelIdReasonForProvider)(modelId, provider);
}
function validateProfileSettingsModelDenylist(profileOrVariant, settings) {
    if (settings.env && typeof settings.env === 'object') {
        for (const key of MODEL_ENV_KEYS) {
            const value = settings.env[key];
            if (typeof value !== 'string')
                continue;
            const deniedReason = findDeniedProfileModel(profileOrVariant, value, settings);
            if (deniedReason) {
                return `${key}: ${deniedReason}`;
            }
        }
    }
    if (!Array.isArray(settings.presets))
        return null;
    for (const preset of settings.presets) {
        for (const key of PRESET_MODEL_KEYS) {
            const value = preset[key];
            if (typeof value !== 'string')
                continue;
            const deniedReason = findDeniedProfileModel(profileOrVariant, value, settings);
            if (deniedReason) {
                const presetName = typeof preset.name === 'string' ? preset.name : 'unnamed';
                return `Preset '${presetName}' (${key}): ${deniedReason}`;
            }
        }
    }
    return null;
}
function canonicalizeProfileSettings(profileOrVariant, settings) {
    const provider = resolveProviderForSettings(profileOrVariant, settings);
    if (!provider)
        return settings;
    let changed = false;
    const next = { ...settings };
    if (settings.env && typeof settings.env === 'object') {
        const env = { ...settings.env };
        for (const key of MODEL_ENV_KEYS) {
            const value = env[key];
            if (typeof value !== 'string')
                continue;
            const canonical = (0, model_id_normalizer_1.canonicalizeModelIdForProvider)(value, provider);
            if (canonical !== value) {
                env[key] = canonical;
                changed = true;
            }
        }
        next.env = env;
    }
    if (Array.isArray(settings.presets)) {
        const normalizedPresets = settings.presets.flatMap((preset) => {
            const normalizedPreset = { ...preset };
            let presetChanged = false;
            for (const key of PRESET_MODEL_KEYS) {
                const value = normalizedPreset[key];
                if (typeof value !== 'string')
                    continue;
                const deniedReason = (0, model_id_normalizer_1.getDeniedModelIdReasonForProvider)(value, provider);
                if (deniedReason) {
                    changed = true;
                    return [];
                }
                const canonical = (0, model_id_normalizer_1.canonicalizeModelIdForProvider)(value, provider);
                if (canonical !== value) {
                    normalizedPreset[key] = canonical;
                    presetChanged = true;
                }
            }
            if (presetChanged)
                changed = true;
            return normalizedPreset;
        });
        next.presets = normalizedPresets;
    }
    return changed ? next : settings;
}
async function resolveImageAnalysisStatusForProfile(profileOrVariant, settings, settingsPath) {
    const variants = (0, variant_service_1.listVariants)();
    const variant = variants[profileOrVariant];
    const cliproxyProvider = resolveProviderForProfile(profileOrVariant);
    const cliproxyBridge = (0, services_1.resolveCliproxyBridgeMetadata)(settings);
    const status = await (0, hooks_1.resolveImageAnalysisRuntimeStatus)({
        profileName: profileOrVariant,
        profileType: cliproxyProvider ? 'cliproxy' : 'settings',
        cliproxyProvider,
        isComposite: Boolean(variant && 'type' in variant && variant.type === 'composite'),
        settingsPath,
        settings,
        cliproxyBridge,
        hookInstalled: (0, image_analyzer_profile_hook_injector_1.hasImageAnalysisProfileHook)(profileOrVariant, settingsPath),
        sharedHookInstalled: (0, image_analyzer_hook_installer_1.hasImageAnalyzerHook)(),
    }, (0, config_loader_facade_1.getImageAnalysisConfig)());
    return {
        ...status,
        persistencePath: status.shouldPersistHook
            ? (0, image_analyzer_profile_hook_injector_1.getImageAnalysisProfileSettingsPath)(profileOrVariant, settingsPath)
            : null,
    };
}
async function resolvePreviewImageAnalysisStatus(profileOrVariant, settings) {
    const normalizedSettings = canonicalizeProfileSettings(profileOrVariant, settings);
    const settingsPath = resolveSettingsPath(profileOrVariant);
    return resolveImageAnalysisStatusForProfile(profileOrVariant, normalizedSettings, settingsPath);
}
function writeSettingsAtomically(settingsPath, settings) {
    requireWritableSettingsPathWithinCcs(settingsPath);
    const tempPath = `${settingsPath}.tmp.${process.pid}`;
    requireWritableSettingsPathWithinCcs(tempPath);
    fs.writeFileSync(tempPath, JSON.stringify(settings, null, 2) + '\n', { flag: 'wx' });
    fs.renameSync(tempPath, settingsPath);
}
function withSettingsFileLock(settingsPath, callback) {
    requireWritableSettingsPathWithinCcs(settingsPath);
    const lockTarget = safeRealPath(settingsPath)
        ? settingsPath
        : findExistingParentRealPath(settingsPath);
    if (!lockTarget) {
        throw new Error('Invalid settings path');
    }
    let release;
    try {
        release = lockfile.lockSync(lockTarget, { stale: 10000 });
        return callback();
    }
    finally {
        if (release) {
            try {
                release();
            }
            catch {
                // Best-effort release
            }
        }
    }
}
function loadCanonicalProfileSettings(profileOrVariant, settingsPath, persist = false, strictPersist = false) {
    if (!requireExistingSettingsPathWithinCcs(settingsPath)) {
        throw new Error('Settings not found');
    }
    const loaded = (0, config_loader_facade_1.loadSettings)(settingsPath);
    const canonicalized = canonicalizeProfileSettings(profileOrVariant, loaded);
    if (persist && canonicalized !== loaded) {
        try {
            writeSettingsAtomically(settingsPath, canonicalized);
        }
        catch (error) {
            if (strictPersist) {
                throw error;
            }
            logRouteError(`Failed to persist canonicalized settings for ${profileOrVariant}`, error);
        }
    }
    return canonicalized;
}
/**
 * Helper: Mask API keys in settings
 */
function maskApiKeys(settings) {
    if (!settings.env)
        return settings;
    const masked = { ...settings, env: { ...settings.env } };
    for (const key of Object.keys(masked.env)) {
        if ((0, sensitive_keys_1.isSensitiveKey)(key)) {
            masked.env[key] = (0, sensitive_keys_1.maskSensitiveValue)(masked.env[key]);
        }
    }
    return masked;
}
/**
 * GET /api/settings/:profile - Get settings with masked API keys
 */
router.get('/:profile', async (req, res) => {
    try {
        const { profile } = req.params;
        const settingsPath = resolveSettingsPath(profile);
        if (!requireExistingSettingsPathWithinCcs(settingsPath)) {
            res.status(404).json({ error: 'Settings not found' });
            return;
        }
        const settings = loadCanonicalProfileSettings(profile, settingsPath, true);
        const stat = fs.statSync(settingsPath);
        const masked = maskApiKeys(settings);
        res.json({
            profile,
            settings: masked,
            mtime: stat.mtime.getTime(),
            path: settingsPath,
            cliproxyBridge: (0, services_1.resolveCliproxyBridgeMetadata)(settings),
            imageAnalysisStatus: await resolveImageAnalysisStatusForProfile(profile, settings, settingsPath),
        });
    }
    catch (error) {
        respondInternalError(res, error, 'Internal server error.');
    }
});
/**
 * GET /api/settings/:profile/raw - Get full settings (for editing)
 */
router.get('/:profile/raw', async (req, res) => {
    if (!requireSensitiveLocalAccess(req, res))
        return;
    try {
        const { profile } = req.params;
        const settingsPath = resolveSettingsPath(profile);
        if (!requireExistingSettingsPathWithinCcs(settingsPath)) {
            res.status(404).json({ error: 'Settings not found' });
            return;
        }
        const settings = loadCanonicalProfileSettings(profile, settingsPath, true);
        const stat = fs.statSync(settingsPath);
        res.json({
            profile,
            settings,
            mtime: stat.mtime.getTime(),
            path: settingsPath,
            cliproxyBridge: (0, services_1.resolveCliproxyBridgeMetadata)(settings),
            imageAnalysisStatus: await resolveImageAnalysisStatusForProfile(profile, settings, settingsPath),
        });
    }
    catch (error) {
        respondInternalError(res, error, 'Internal server error.');
    }
});
/**
 * POST /api/settings/:profile/image-analysis-status - Preview image analysis status from editor JSON
 */
router.post('/:profile/image-analysis-status', async (req, res) => {
    if (!requireSensitiveLocalAccess(req, res))
        return;
    try {
        const { profile } = req.params;
        const { settings } = req.body;
        if (!settings || typeof settings !== 'object') {
            res.status(400).json({ error: 'settings object is required in request body' });
            return;
        }
        res.json({
            imageAnalysisStatus: await resolvePreviewImageAnalysisStatus(profile, settings),
        });
    }
    catch (error) {
        respondInternalError(res, error, 'Internal server error.');
    }
});
/** Required env vars for CLIProxy providers to function */
const REQUIRED_ENV_KEYS = ['ANTHROPIC_BASE_URL', 'ANTHROPIC_AUTH_TOKEN'];
/** Check if settings have required fields (returns missing list for warnings) */
function checkRequiredEnvVars(settings) {
    const env = settings?.env || {};
    if (env.ANTHROPIC_API_KEY?.trim() && !env.ANTHROPIC_BASE_URL?.trim()) {
        return [];
    }
    return REQUIRED_ENV_KEYS.filter((key) => !env[key]?.trim());
}
/**
 * PUT /api/settings/:profile - Update settings with conflict detection and backup
 */
router.put('/:profile', (req, res) => {
    if (!requireSensitiveLocalAccess(req, res))
        return;
    try {
        const { profile } = req.params;
        const { settings, expectedMtime } = req.body;
        // Validate settings object exists
        if (!settings || typeof settings !== 'object') {
            res.status(400).json({ error: 'settings object is required in request body' });
            return;
        }
        const deniedModelReason = validateProfileSettingsModelDenylist(profile, settings);
        if (deniedModelReason) {
            res.status(400).json({ error: deniedModelReason });
            return;
        }
        const normalizedSettings = canonicalizeProfileSettings(profile, settings);
        // Deduplicate CCS hooks to prevent accumulation (fixes #450)
        // This handles cases where duplicate hooks were added by previous versions
        (0, hook_utils_1.deduplicateCcsHooks)(normalizedSettings);
        (0, image_analyzer_hook_utils_1.removeCcsImageAnalyzerHooks)(normalizedSettings);
        const ccsDir = (0, config_loader_facade_1.getCcsDir)();
        // Check for missing required fields (warning, not blocking - runtime fills defaults)
        const missingFields = checkRequiredEnvVars(normalizedSettings);
        const settingsPath = resolveSettingsPath(profile);
        let backupPath;
        let created = false;
        let newMtime = 0;
        withSettingsFileLock(settingsPath, () => {
            const fileExists = fs.existsSync(settingsPath);
            if (fileExists && expectedMtime) {
                const stat = fs.statSync(settingsPath);
                if (stat.mtime.getTime() !== expectedMtime) {
                    res.status(409).json({
                        error: 'File modified externally',
                        currentMtime: stat.mtime.getTime(),
                    });
                    return;
                }
            }
            const newContent = JSON.stringify(normalizedSettings, null, 2) + '\n';
            if (fileExists) {
                const existingContent = fs.readFileSync(settingsPath, 'utf8');
                if (existingContent !== newContent) {
                    const backupDir = path.join(ccsDir, 'backups');
                    requireWritableSettingsPathWithinCcs(path.join(backupDir, '.settings-backup-probe'));
                    if (!fs.existsSync(backupDir)) {
                        fs.mkdirSync(backupDir, { recursive: true });
                    }
                    const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
                    backupPath = path.join(backupDir, `${profile}.${timestamp}.settings.json`);
                    requireWritableSettingsPathWithinCcs(backupPath);
                    fs.copyFileSync(settingsPath, backupPath);
                }
            }
            else {
                created = true;
                fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
            }
            const tempPath = `${settingsPath}.tmp.${process.pid}`;
            requireWritableSettingsPathWithinCcs(tempPath);
            fs.writeFileSync(tempPath, newContent, { flag: 'wx' });
            fs.renameSync(tempPath, settingsPath);
            newMtime = fs.statSync(settingsPath).mtime.getTime();
        });
        if (res.headersSent) {
            return;
        }
        res.json({
            profile,
            mtime: newMtime,
            backupPath,
            created,
            // Include warning if fields missing (runtime will use defaults)
            ...(missingFields.length > 0 && {
                warning: `Missing fields will use defaults: ${missingFields.join(', ')}`,
                missingFields,
            }),
        });
    }
    catch (error) {
        respondInternalError(res, error, 'Internal server error.');
    }
});
// ==================== Presets ====================
/**
 * GET /api/settings/:profile/presets - Get saved presets for a provider
 */
router.get('/:profile/presets', (req, res) => {
    try {
        const { profile } = req.params;
        const settingsPath = resolveSettingsPath(profile);
        if (!requireExistingSettingsPathWithinCcs(settingsPath)) {
            res.json({ presets: [] });
            return;
        }
        const settings = loadCanonicalProfileSettings(profile, settingsPath, true);
        res.json({ presets: settings.presets || [] });
    }
    catch (error) {
        respondInternalError(res, error, 'Internal server error.');
    }
});
/**
 * POST /api/settings/:profile/presets - Create a new preset
 */
router.post('/:profile/presets', (req, res) => {
    if (!requireSensitiveLocalAccess(req, res))
        return;
    try {
        const { profile } = req.params;
        const { name, default: defaultModel, opus, sonnet, haiku } = req.body;
        if (!name || !defaultModel) {
            res.status(400).json({ error: 'Missing required fields: name, default' });
            return;
        }
        const settingsPath = resolveSettingsPath(profile);
        let persistedPreset;
        withSettingsFileLock(settingsPath, () => {
            if (!fs.existsSync(settingsPath)) {
                fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
                fs.writeFileSync(settingsPath, JSON.stringify({ env: {}, presets: [] }, null, 2) + '\n');
            }
            const settings = loadCanonicalProfileSettings(profile, settingsPath, false);
            settings.presets = settings.presets || [];
            if (settings.presets.some((p) => p.name === name)) {
                res.status(409).json({ error: 'Preset with this name already exists' });
                return;
            }
            const normalizePresetModel = (modelId) => canonicalizeProfileModelId(profile, modelId, settings);
            for (const modelId of [
                defaultModel,
                opus || defaultModel,
                sonnet || defaultModel,
                haiku || defaultModel,
            ]) {
                const deniedReason = findDeniedProfileModel(profile, modelId, settings);
                if (deniedReason) {
                    res.status(400).json({ error: deniedReason });
                    return;
                }
            }
            const preset = {
                name,
                default: normalizePresetModel(defaultModel),
                opus: normalizePresetModel(opus || defaultModel),
                sonnet: normalizePresetModel(sonnet || defaultModel),
                haiku: normalizePresetModel(haiku || defaultModel),
            };
            settings.presets.push(preset);
            const canonicalizedSettings = canonicalizeProfileSettings(profile, settings);
            writeSettingsAtomically(settingsPath, canonicalizedSettings);
            persistedPreset =
                canonicalizedSettings.presets?.find((entry) => entry.name === name) || preset;
        });
        if (res.headersSent) {
            return;
        }
        res.status(201).json({ preset: persistedPreset });
    }
    catch (error) {
        respondInternalError(res, error, 'Internal server error.');
    }
});
/**
 * DELETE /api/settings/:profile/presets/:name - Delete a preset
 */
router.delete('/:profile/presets/:name', (req, res) => {
    if (!requireSensitiveLocalAccess(req, res))
        return;
    try {
        const { profile, name } = req.params;
        const settingsPath = resolveSettingsPath(profile);
        withSettingsFileLock(settingsPath, () => {
            if (!fs.existsSync(settingsPath)) {
                res.status(404).json({ error: 'Settings not found' });
                return;
            }
            const settings = loadCanonicalProfileSettings(profile, settingsPath, false);
            if (!settings.presets || !settings.presets.some((p) => p.name === name)) {
                res.status(404).json({ error: 'Preset not found' });
                return;
            }
            settings.presets = settings.presets.filter((p) => p.name !== name);
            const canonicalizedSettings = canonicalizeProfileSettings(profile, settings);
            writeSettingsAtomically(settingsPath, canonicalizedSettings);
        });
        if (res.headersSent) {
            return;
        }
        res.json({ success: true });
    }
    catch (error) {
        respondInternalError(res, error, 'Internal server error.');
    }
});
// ==================== Auth Tokens ====================
/**
 * GET /api/settings/auth/antigravity-risk - Get shared power user bypass setting
 */
router.get('/auth/antigravity-risk', (req, res) => {
    if (!requireSensitiveLocalAccess(req, res))
        return;
    try {
        const config = (0, config_loader_facade_1.loadOrCreateUnifiedConfig)();
        res.json({
            antigravityAckBypass: config.cliproxy?.safety?.antigravity_ack_bypass === true,
        });
    }
    catch (error) {
        respondInternalError(res, error, 'Failed to load power user mode.');
    }
});
/**
 * PUT /api/settings/auth/antigravity-risk - Update shared power user bypass setting
 */
router.put('/auth/antigravity-risk', (req, res) => {
    if (!requireSensitiveLocalAccess(req, res))
        return;
    try {
        const body = req.body;
        const antigravityAckBypass = body && typeof body === 'object' ? body.antigravityAckBypass : undefined;
        if (typeof antigravityAckBypass !== 'boolean') {
            res.status(400).json({ error: 'antigravityAckBypass must be a boolean' });
            return;
        }
        const updatedConfig = (0, config_loader_facade_1.mutateConfig)((config) => {
            config.cliproxy.safety = {
                ...(config.cliproxy.safety ?? {}),
                antigravity_ack_bypass: antigravityAckBypass,
            };
        });
        res.json({
            success: true,
            antigravityAckBypass: updatedConfig.cliproxy?.safety?.antigravity_ack_bypass === true,
        });
    }
    catch (error) {
        const classified = classifyConfigSaveFailure(error);
        respondInternalError(res, error, classified.message, classified.statusCode);
    }
});
/**
 * GET /api/settings/auth/tokens - Get current auth token status (masked)
 */
router.get('/auth/tokens', (_req, res) => {
    if (!requireSensitiveLocalAccess(_req, res))
        return;
    try {
        const summary = (0, cliproxy_1.getAuthSummary)();
        res.json({
            apiKey: {
                value: (0, cliproxy_1.maskToken)(summary.apiKey.value),
                isCustom: summary.apiKey.isCustom,
            },
            managementSecret: {
                value: (0, cliproxy_1.maskToken)(summary.managementSecret.value),
                isCustom: summary.managementSecret.isCustom,
            },
        });
    }
    catch (error) {
        respondInternalError(res, error, 'Internal server error.');
    }
});
/**
 * GET /api/settings/auth/tokens/raw - Get current auth tokens unmasked
 * NOTE: Sensitive endpoint - no caching, localhost only
 */
router.get('/auth/tokens/raw', (req, res) => {
    if (!requireSensitiveLocalAccess(req, res))
        return;
    try {
        // Prevent caching of sensitive data
        res.setHeader('Cache-Control', 'no-store');
        const summary = (0, cliproxy_1.getAuthSummary)();
        res.json({
            apiKey: {
                value: summary.apiKey.value,
                isCustom: summary.apiKey.isCustom,
            },
            managementSecret: {
                value: summary.managementSecret.value,
                isCustom: summary.managementSecret.isCustom,
            },
        });
    }
    catch (error) {
        respondInternalError(res, error, 'Failed to load raw auth tokens.');
    }
});
/**
 * PUT /api/settings/auth/tokens - Update auth tokens
 */
router.put('/auth/tokens', (req, res) => {
    if (!requireSensitiveLocalAccess(req, res))
        return;
    try {
        const { apiKey, managementSecret } = req.body;
        if (apiKey !== undefined) {
            (0, cliproxy_1.setGlobalApiKey)(apiKey || undefined);
        }
        if (managementSecret !== undefined) {
            (0, cliproxy_1.setGlobalManagementSecret)(managementSecret || undefined);
        }
        // Regenerate CLIProxy config to apply changes
        (0, config_generator_1.regenerateConfig)();
        const summary = (0, cliproxy_1.getAuthSummary)();
        res.json({
            success: true,
            apiKey: {
                value: (0, cliproxy_1.maskToken)(summary.apiKey.value),
                isCustom: summary.apiKey.isCustom,
            },
            managementSecret: {
                value: (0, cliproxy_1.maskToken)(summary.managementSecret.value),
                isCustom: summary.managementSecret.isCustom,
            },
            message: 'Restart CLIProxy to apply changes',
        });
    }
    catch (error) {
        respondInternalError(res, error, 'Internal server error.');
    }
});
/**
 * POST /api/settings/auth/tokens/regenerate-secret - Generate new management secret
 */
router.post('/auth/tokens/regenerate-secret', (_req, res) => {
    if (!requireSensitiveLocalAccess(_req, res))
        return;
    try {
        const newSecret = (0, cliproxy_1.generateSecureToken)(32);
        (0, cliproxy_1.setGlobalManagementSecret)(newSecret);
        // Regenerate CLIProxy config to apply changes
        (0, config_generator_1.regenerateConfig)();
        res.json({
            success: true,
            managementSecret: {
                value: (0, cliproxy_1.maskToken)(newSecret),
                isCustom: true,
            },
            message: 'Restart CLIProxy to apply changes',
        });
    }
    catch (error) {
        respondInternalError(res, error, 'Internal server error.');
    }
});
/**
 * POST /api/settings/auth/tokens/reset - Reset auth tokens to defaults
 */
router.post('/auth/tokens/reset', (_req, res) => {
    if (!requireSensitiveLocalAccess(_req, res))
        return;
    try {
        (0, cliproxy_1.resetAuthToDefaults)();
        // Regenerate CLIProxy config to apply changes
        (0, config_generator_1.regenerateConfig)();
        const summary = (0, cliproxy_1.getAuthSummary)();
        res.json({
            success: true,
            apiKey: {
                value: (0, cliproxy_1.maskToken)(summary.apiKey.value),
                isCustom: summary.apiKey.isCustom,
            },
            managementSecret: {
                value: (0, cliproxy_1.maskToken)(summary.managementSecret.value),
                isCustom: summary.managementSecret.isCustom,
            },
            message: 'Tokens reset to defaults. Restart CLIProxy to apply.',
        });
    }
    catch (error) {
        respondInternalError(res, error, 'Internal server error.');
    }
});
exports.default = router;
//# sourceMappingURL=settings-routes.js.map