"use strict";
/**
 * CLIProxy Variant Service
 *
 * Handles CRUD operations for CLIProxy variant profiles.
 * Supports both unified config (config.yaml) and legacy JSON format.
 */
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 });
exports.updateCompositeVariant = exports.createCompositeVariant = exports.updateVariant = exports.removeVariant = exports.createVariant = exports.listVariants = exports.variantExists = exports.validateProviderBackend = exports.validateProfileName = void 0;
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const types_1 = require("../types");
const reserved_names_1 = require("../../config/reserved-names");
const config_generator_1 = require("../config/config-generator");
const session_tracker_1 = require("../session-tracker");
const ui_1 = require("../../utils/ui");
const composite_validator_1 = require("../config/composite-validator");
const model_id_normalizer_1 = require("../ai-providers/model-id-normalizer");
const variant_settings_1 = require("./variant-settings");
const variant_config_adapter_1 = require("./variant-config-adapter");
const binary_manager_1 = require("../binary-manager");
const config_loader_facade_1 = require("../../config/config-loader-facade");
/**
 * Validate CLIProxy profile name
 */
function validateProfileName(name) {
    if (!name) {
        return 'Profile name is required';
    }
    if (!/^[a-zA-Z][a-zA-Z0-9._-]*$/.test(name)) {
        return 'Name must start with letter, contain only letters, numbers, dot, dash, underscore';
    }
    if (name.length > 32) {
        return 'Name must be 32 characters or less';
    }
    if ((0, reserved_names_1.isReservedName)(name)) {
        return `'${name}' is a reserved name`;
    }
    if ((0, reserved_names_1.isWindowsReservedName)(name)) {
        return `'${name}' is a Windows reserved device name and cannot be used`;
    }
    return null;
}
exports.validateProfileName = validateProfileName;
/**
 * Validate provider/backend compatibility
 * Returns an error message if a provider requires Plus while local CLIProxy is
 * configured for the default original backend.
 */
function validateProviderBackend(provider) {
    // Normalize provider to lowercase for case-insensitive comparison
    const normalizedProvider = provider.toLowerCase();
    const backend = (0, binary_manager_1.getConfiguredBackend)();
    if (backend === 'original' && types_1.PLUS_ONLY_PROVIDERS.includes(normalizedProvider)) {
        return (0, binary_manager_1.getPlusBackendUnavailableMessage)(provider);
    }
    return null;
}
exports.validateProviderBackend = validateProviderBackend;
/**
 * Check if CLIProxy variant profile exists
 */
function variantExists(name) {
    return (0, variant_config_adapter_1.variantExistsInConfig)(name);
}
exports.variantExists = variantExists;
/**
 * List all CLIProxy variants
 */
function listVariants() {
    return (0, variant_config_adapter_1.listVariantsFromConfig)();
}
exports.listVariants = listVariants;
/**
 * Create a new CLIProxy variant
 */
function createVariant(name, provider, model, account, target = 'claude') {
    try {
        const canonicalModel = (0, model_id_normalizer_1.canonicalizeModelIdForProvider)(model, provider);
        const deniedModelReason = (0, model_id_normalizer_1.getDeniedModelIdReasonForProvider)(canonicalModel, provider);
        if (deniedModelReason) {
            return { success: false, error: deniedModelReason };
        }
        // Validate provider/backend compatibility (block kiro/ghcp on original backend)
        const backendError = validateProviderBackend(provider);
        if (backendError) {
            return { success: false, error: backendError };
        }
        // Allocate unique port for this variant
        const port = (0, variant_config_adapter_1.getNextAvailablePort)();
        let settingsPath;
        if ((0, config_loader_facade_1.isUnifiedMode)()) {
            settingsPath = (0, variant_settings_1.createSettingsFileUnified)(name, provider, canonicalModel, port);
            (0, variant_config_adapter_1.saveVariantUnified)(name, provider, (0, variant_settings_1.getRelativeSettingsPath)(provider, name), account, port, target);
        }
        else {
            settingsPath = (0, variant_settings_1.createSettingsFile)(name, provider, canonicalModel, port);
            (0, variant_config_adapter_1.saveVariantLegacy)(name, provider, `~/.ccs/${path.basename(settingsPath)}`, account, port, target);
        }
        return {
            success: true,
            settingsPath,
            variant: { provider, model: canonicalModel, account, port, target },
        };
    }
    catch (error) {
        return {
            success: false,
            error: error.message,
        };
    }
}
exports.createVariant = createVariant;
/**
 * Remove a CLIProxy variant
 */
function removeVariant(name) {
    try {
        // First check if variant exists and has active sessions
        const variants = (0, variant_config_adapter_1.listVariantsFromConfig)();
        const existingVariant = variants[name];
        if (!existingVariant) {
            return { success: false, error: `Variant '${name}' not found` };
        }
        // Check for active sessions on this variant's port before deletion
        if (existingVariant.port && (0, session_tracker_1.hasActiveSessions)(existingVariant.port)) {
            return {
                success: false,
                error: `Cannot delete variant '${name}': CLIProxy is running with active sessions. Stop the session first.`,
            };
        }
        let variant;
        if ((0, config_loader_facade_1.isUnifiedMode)()) {
            const unifiedVariant = (0, variant_config_adapter_1.removeVariantFromUnifiedConfig)(name);
            if (unifiedVariant?.settings) {
                (0, variant_settings_1.deleteSettingsFile)(unifiedVariant.settings);
            }
            // Clean up port-specific config and session files
            if (unifiedVariant?.port) {
                (0, config_generator_1.deleteConfigForPort)(unifiedVariant.port);
                (0, session_tracker_1.deleteSessionLockForPort)(unifiedVariant.port);
            }
            variant = unifiedVariant;
        }
        else {
            variant = (0, variant_config_adapter_1.removeVariantFromLegacyConfig)(name);
            if (variant?.settings) {
                (0, variant_settings_1.deleteSettingsFile)(variant.settings);
            }
            // Clean up port-specific config and session files
            if (variant?.port) {
                (0, config_generator_1.deleteConfigForPort)(variant.port);
                (0, session_tracker_1.deleteSessionLockForPort)(variant.port);
            }
        }
        return { success: true, variant: variant ?? undefined };
    }
    catch (error) {
        return { success: false, error: error.message };
    }
}
exports.removeVariant = removeVariant;
/**
 * Update an existing CLIProxy variant
 */
function updateVariant(name, updates) {
    try {
        const variants = (0, variant_config_adapter_1.listVariantsFromConfig)();
        const existing = variants[name];
        if (!existing) {
            return { success: false, error: `Variant '${name}' not found` };
        }
        if (existing.type === 'composite') {
            console.log((0, ui_1.warn)('Cannot update composite variant properties directly. Remove and recreate, or edit config.yaml.'));
            return { success: false, error: 'Composite variant update not supported' };
        }
        const providerChanged = updates.provider !== undefined && updates.provider !== existing.provider;
        const existingTarget = existing.target || 'claude';
        const targetChanged = updates.target !== undefined && updates.target !== existingTarget;
        const hasModelUpdate = updates.model !== undefined && updates.model.trim().length > 0;
        const providerForModelUpdate = (updates.provider ?? existing.provider);
        const canonicalModelUpdate = updates.model !== undefined
            ? (0, model_id_normalizer_1.canonicalizeModelIdForProvider)(updates.model.trim(), providerForModelUpdate)
            : undefined;
        if (providerChanged && !hasModelUpdate) {
            return {
                success: false,
                error: 'Changing provider requires model update in the same request',
            };
        }
        if (hasModelUpdate) {
            const deniedModelReason = (0, model_id_normalizer_1.getDeniedModelIdReasonForProvider)(canonicalModelUpdate || '', providerForModelUpdate);
            if (deniedModelReason) {
                return { success: false, error: deniedModelReason };
            }
        }
        if (updates.provider !== undefined) {
            const backendError = validateProviderBackend(updates.provider);
            if (backendError) {
                return { success: false, error: backendError };
            }
        }
        // Update settings file
        if (existing.settings) {
            const settingsPath = existing.settings.replace(/^~/, os.homedir());
            if (providerChanged) {
                (0, variant_settings_1.updateSettingsProviderAndModel)(settingsPath, updates.provider, canonicalModelUpdate || '', existing.port);
            }
            else if (updates.model !== undefined) {
                (0, variant_settings_1.updateSettingsModel)(settingsPath, canonicalModelUpdate || '', existing.provider);
            }
        }
        // Update config entry if provider/account/target changed
        if (updates.provider !== undefined || updates.account !== undefined || targetChanged) {
            const newProvider = updates.provider ?? existing.provider;
            const newAccount = updates.account !== undefined ? updates.account : existing.account;
            const newTarget = updates.target ?? existingTarget;
            if ((0, config_loader_facade_1.isUnifiedMode)()) {
                (0, variant_config_adapter_1.saveVariantUnified)(name, newProvider, existing.settings || '', newAccount || undefined, existing.port, newTarget);
            }
            else {
                (0, variant_config_adapter_1.saveVariantLegacy)(name, newProvider, existing.settings || '', newAccount || undefined, existing.port, newTarget);
            }
        }
        return {
            success: true,
            variant: {
                provider: updates.provider ?? existing.provider,
                model: canonicalModelUpdate || existing.model,
                account: updates.account !== undefined ? updates.account : existing.account,
                port: existing.port,
                settings: existing.settings,
                target: updates.target ?? existingTarget,
            },
        };
    }
    catch (error) {
        return { success: false, error: error.message };
    }
}
exports.updateVariant = updateVariant;
/**
 * Create a new composite CLIProxy variant.
 * Mixes different providers per tier using CLIProxyAPI root endpoints.
 */
function createCompositeVariant(options) {
    if (!(0, config_loader_facade_1.isUnifiedMode)()) {
        throw new Error('Composite variants require unified config (config.yaml). Run "ccs migrate" first.');
    }
    try {
        const { name, defaultTier, tiers, target = 'claude' } = options;
        const validationError = (0, composite_validator_1.validateCompositeTiers)(tiers, {
            defaultTier,
            requireAllTiers: true,
        });
        if (validationError) {
            return { success: false, error: validationError };
        }
        // Validate all tier providers against backend compatibility
        const tierNames = ['opus', 'sonnet', 'haiku'];
        for (const tier of tierNames) {
            const backendError = validateProviderBackend(tiers[tier].provider);
            if (backendError) {
                return { success: false, error: `${tier} tier: ${backendError}` };
            }
        }
        // Allocate unique port for this composite variant
        const port = (0, variant_config_adapter_1.getNextAvailablePort)();
        // Create settings file with root URL + per-tier models
        const settingsPath = (0, variant_settings_1.createCompositeSettingsFile)(name, tiers, defaultTier, port);
        // Save composite config to unified config
        const compositeConfig = {
            type: 'composite',
            default_tier: defaultTier,
            tiers,
            settings: settingsPath,
            port,
            ...(target !== 'claude' && { target }),
        };
        (0, variant_config_adapter_1.saveCompositeVariantUnified)(name, compositeConfig);
        return {
            success: true,
            settingsPath,
            variant: {
                provider: tiers[defaultTier].provider,
                type: 'composite',
                default_tier: defaultTier,
                tiers,
                port,
                target,
            },
        };
    }
    catch (error) {
        return { success: false, error: error.message };
    }
}
exports.createCompositeVariant = createCompositeVariant;
/**
 * Update an existing composite CLIProxy variant.
 * Merges changes with existing config and regenerates settings file.
 */
function updateCompositeVariant(name, updates) {
    if (!(0, config_loader_facade_1.isUnifiedMode)()) {
        throw new Error('Composite variants require unified config (config.yaml).');
    }
    try {
        const variants = (0, variant_config_adapter_1.listVariantsFromConfig)();
        const existing = variants[name];
        if (!existing) {
            return { success: false, error: `Variant '${name}' not found` };
        }
        if (existing.type !== 'composite' || !existing.tiers) {
            return { success: false, error: `Variant '${name}' is not a composite variant` };
        }
        // Deep merge tiers to preserve optional fields (fallback, thinking, account)
        const mergedTiers = {
            opus: { ...existing.tiers.opus, ...updates.tiers?.opus },
            sonnet: { ...existing.tiers.sonnet, ...updates.tiers?.sonnet },
            haiku: { ...existing.tiers.haiku, ...updates.tiers?.haiku },
        };
        const newDefaultTier = updates.defaultTier ?? existing.default_tier ?? 'sonnet';
        const existingTarget = existing.target || 'claude';
        const newTarget = updates.target ?? existingTarget;
        const validationError = (0, composite_validator_1.validateCompositeTiers)(mergedTiers, {
            defaultTier: newDefaultTier,
            requireAllTiers: true,
        });
        if (validationError) {
            return { success: false, error: validationError };
        }
        // Validate all tier providers against backend compatibility
        const tierNames = ['opus', 'sonnet', 'haiku'];
        for (const tier of tierNames) {
            const backendError = validateProviderBackend(mergedTiers[tier].provider);
            if (backendError) {
                return { success: false, error: `${tier} tier: ${backendError}` };
            }
        }
        // Preserve existing settings path when configured; otherwise use default path.
        const settingsRef = existing.settings || path.join((0, config_loader_facade_1.getCcsDir)(), `composite-${name}.settings.json`);
        // Create new settings file with updated config
        const settingsPath = (0, variant_settings_1.createCompositeSettingsFile)(name, mergedTiers, newDefaultTier, existing.port, settingsRef);
        // Save updated composite config to unified config
        const compositeConfig = {
            type: 'composite',
            default_tier: newDefaultTier,
            tiers: mergedTiers,
            settings: settingsRef,
            port: existing.port,
            ...(newTarget !== 'claude' && { target: newTarget }),
        };
        (0, variant_config_adapter_1.saveCompositeVariantUnified)(name, compositeConfig);
        return {
            success: true,
            settingsPath,
            variant: {
                provider: mergedTiers[newDefaultTier].provider,
                type: 'composite',
                default_tier: newDefaultTier,
                tiers: mergedTiers,
                port: existing.port,
                settings: settingsRef,
                target: newTarget,
            },
        };
    }
    catch (error) {
        return { success: false, error: error.message };
    }
}
exports.updateCompositeVariant = updateCompositeVariant;
//# sourceMappingURL=variant-service.js.map