/**
 * LiteLLM API Configuration Manager
 * Manages provider credentials, custom endpoints, and cache settings
 */
import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';
import { GlobalPaths, ensureStorageDir } from './storage-paths.js';
import { addClaudeApiEndpoint, removeClaudeApiEndpoint } from '../tools/claude-cli-tools.js';
/**
 * Default configuration
 */
function getDefaultConfig() {
    return {
        version: 1,
        providers: [],
        endpoints: [],
        globalCacheSettings: {
            enabled: true,
            cacheDir: '~/.ccw/cache/context',
            maxTotalSizeMB: 100,
        },
    };
}
/**
 * Get config file path (global, shared across all projects)
 */
function getConfigPath(_baseDir) {
    const configDir = GlobalPaths.config();
    ensureStorageDir(configDir);
    return join(configDir, 'litellm-api-config.json');
}
function bestEffortRestrictPermissions(filePath, mode) {
    try {
        chmodSync(filePath, mode);
    }
    catch {
        // Ignore permission errors (e.g., Windows or restrictive environments)
    }
}
/**
 * Load configuration from file
 */
export function loadLiteLLMApiConfig(baseDir) {
    const configPath = getConfigPath(baseDir);
    if (!existsSync(configPath)) {
        return getDefaultConfig();
    }
    try {
        const content = readFileSync(configPath, 'utf-8');
        return JSON.parse(content);
    }
    catch (error) {
        console.error('[LiteLLM Config] Failed to load config:', error);
        return getDefaultConfig();
    }
}
/**
 * Save configuration to file
 */
function saveConfig(baseDir, config) {
    const configPath = getConfigPath(baseDir);
    writeFileSync(configPath, JSON.stringify(config, null, 2), { encoding: 'utf8', mode: 0o600 });
    bestEffortRestrictPermissions(configPath, 0o600);
}
/**
 * Resolve environment variables in API key
 * Supports ${ENV_VAR} syntax
 */
export function resolveEnvVar(value) {
    if (!value)
        return value;
    const envVarMatch = value.match(/^\$\{(.+)\}$/);
    if (envVarMatch) {
        const envVarName = envVarMatch[1];
        return process.env[envVarName] || '';
    }
    return value;
}
// ===========================
// Provider Management
// ===========================
/**
 * Get all providers
 */
export function getAllProviders(baseDir) {
    const config = loadLiteLLMApiConfig(baseDir);
    return config.providers;
}
/**
 * Get provider by ID
 */
export function getProvider(baseDir, providerId) {
    const config = loadLiteLLMApiConfig(baseDir);
    return config.providers.find((p) => p.id === providerId) || null;
}
/**
 * Get provider with resolved environment variables
 */
export function getProviderWithResolvedEnvVars(baseDir, providerId) {
    const provider = getProvider(baseDir, providerId);
    if (!provider)
        return null;
    const resolvedApiKey = resolveEnvVar(provider.apiKey);
    // Avoid leaking env-var syntax or secrets if this object is logged/serialized.
    const sanitizedProvider = {
        ...provider,
        apiKey: '***',
        apiKeys: provider.apiKeys?.map(keyEntry => ({
            ...keyEntry,
            key: '***',
        })),
    };
    Object.defineProperty(sanitizedProvider, 'resolvedApiKey', {
        value: resolvedApiKey,
        enumerable: false,
        writable: false,
        configurable: false,
    });
    return sanitizedProvider;
}
/**
 * Add new provider
 */
export function addProvider(baseDir, providerData) {
    const config = loadLiteLLMApiConfig(baseDir);
    const provider = {
        ...providerData,
        id: `${providerData.type}-${Date.now()}`,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString(),
    };
    config.providers.push(provider);
    saveConfig(baseDir, config);
    return provider;
}
/**
 * Update provider
 */
export function updateProvider(baseDir, providerId, updates) {
    const config = loadLiteLLMApiConfig(baseDir);
    const providerIndex = config.providers.findIndex((p) => p.id === providerId);
    if (providerIndex === -1) {
        throw new Error(`Provider not found: ${providerId}`);
    }
    config.providers[providerIndex] = {
        ...config.providers[providerIndex],
        ...updates,
        updatedAt: new Date().toISOString(),
    };
    saveConfig(baseDir, config);
    return config.providers[providerIndex];
}
/**
 * Delete provider
 */
export function deleteProvider(baseDir, providerId) {
    const config = loadLiteLLMApiConfig(baseDir);
    const initialLength = config.providers.length;
    config.providers = config.providers.filter((p) => p.id !== providerId);
    if (config.providers.length === initialLength) {
        return false;
    }
    // Also remove endpoints using this provider
    config.endpoints = config.endpoints.filter((e) => e.providerId !== providerId);
    saveConfig(baseDir, config);
    return true;
}
// ===========================
// Endpoint Management
// ===========================
/**
 * Get all endpoints
 */
export function getAllEndpoints(baseDir) {
    const config = loadLiteLLMApiConfig(baseDir);
    return config.endpoints;
}
/**
 * Get endpoint by ID
 */
export function getEndpoint(baseDir, endpointId) {
    const config = loadLiteLLMApiConfig(baseDir);
    return config.endpoints.find((e) => e.id === endpointId) || null;
}
/**
 * Find endpoint by ID (alias for getEndpoint)
 */
export function findEndpointById(baseDir, endpointId) {
    return getEndpoint(baseDir, endpointId);
}
/**
 * Add new endpoint
 */
export function addEndpoint(baseDir, endpointData) {
    const config = loadLiteLLMApiConfig(baseDir);
    // Check if ID already exists
    if (config.endpoints.some((e) => e.id === endpointData.id)) {
        throw new Error(`Endpoint ID already exists: ${endpointData.id}`);
    }
    // Verify provider exists
    if (!config.providers.find((p) => p.id === endpointData.providerId)) {
        throw new Error(`Provider not found: ${endpointData.providerId}`);
    }
    const endpoint = {
        ...endpointData,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString(),
    };
    config.endpoints.push(endpoint);
    saveConfig(baseDir, config);
    // Sync to cli-tools.json as api-endpoint type
    // Usage: ccw cli -p "..." --tool <endpoint-id>
    try {
        addClaudeApiEndpoint(homedir(), {
            id: endpoint.id,
            name: endpoint.id, // Use endpoint ID as tool name for CLI access
            enabled: endpoint.enabled !== false
        });
        console.log(`[LiteLLM Config] Synced endpoint ${endpoint.id} to cli-tools.json (api-endpoint)`);
    }
    catch (syncError) {
        console.warn(`[LiteLLM Config] Failed to sync endpoint to cli-tools.json: ${syncError}`);
        // Non-fatal: continue even if sync fails
    }
    return endpoint;
}
/**
 * Update endpoint
 */
export function updateEndpoint(baseDir, endpointId, updates) {
    const config = loadLiteLLMApiConfig(baseDir);
    const endpointIndex = config.endpoints.findIndex((e) => e.id === endpointId);
    if (endpointIndex === -1) {
        throw new Error(`Endpoint not found: ${endpointId}`);
    }
    // Verify provider exists if updating providerId
    if (updates.providerId && !config.providers.find((p) => p.id === updates.providerId)) {
        throw new Error(`Provider not found: ${updates.providerId}`);
    }
    config.endpoints[endpointIndex] = {
        ...config.endpoints[endpointIndex],
        ...updates,
        updatedAt: new Date().toISOString(),
    };
    saveConfig(baseDir, config);
    // Sync enabled status to cli-tools.json
    const updatedEndpoint = config.endpoints[endpointIndex];
    try {
        addClaudeApiEndpoint(homedir(), {
            id: updatedEndpoint.id,
            name: updatedEndpoint.id,
            enabled: updatedEndpoint.enabled !== false
        });
        console.log(`[LiteLLM Config] Synced endpoint ${updatedEndpoint.id} update to cli-tools.json`);
    }
    catch (syncError) {
        console.warn(`[LiteLLM Config] Failed to sync endpoint update to cli-tools.json: ${syncError}`);
    }
    return updatedEndpoint;
}
/**
 * Delete endpoint
 */
export function deleteEndpoint(baseDir, endpointId) {
    const config = loadLiteLLMApiConfig(baseDir);
    const initialLength = config.endpoints.length;
    config.endpoints = config.endpoints.filter((e) => e.id !== endpointId);
    if (config.endpoints.length === initialLength) {
        return false;
    }
    // Clear default endpoint if deleted
    if (config.defaultEndpoint === endpointId) {
        delete config.defaultEndpoint;
    }
    saveConfig(baseDir, config);
    // Remove from cli-tools.json
    try {
        removeClaudeApiEndpoint(homedir(), endpointId);
        console.log(`[LiteLLM Config] Removed endpoint ${endpointId} from cli-tools.json`);
    }
    catch (syncError) {
        console.warn(`[LiteLLM Config] Failed to remove endpoint from cli-tools.json: ${syncError}`);
    }
    return true;
}
// ===========================
// Default Endpoint Management
// ===========================
/**
 * Get default endpoint
 */
export function getDefaultEndpoint(baseDir) {
    const config = loadLiteLLMApiConfig(baseDir);
    return config.defaultEndpoint;
}
/**
 * Set default endpoint
 */
export function setDefaultEndpoint(baseDir, endpointId) {
    const config = loadLiteLLMApiConfig(baseDir);
    if (endpointId) {
        // Verify endpoint exists
        if (!config.endpoints.find((e) => e.id === endpointId)) {
            throw new Error(`Endpoint not found: ${endpointId}`);
        }
        config.defaultEndpoint = endpointId;
    }
    else {
        delete config.defaultEndpoint;
    }
    saveConfig(baseDir, config);
}
// ===========================
// Cache Settings Management
// ===========================
/**
 * Get global cache settings
 */
export function getGlobalCacheSettings(baseDir) {
    const config = loadLiteLLMApiConfig(baseDir);
    return config.globalCacheSettings;
}
/**
 * Update global cache settings
 */
export function updateGlobalCacheSettings(baseDir, settings) {
    const config = loadLiteLLMApiConfig(baseDir);
    config.globalCacheSettings = {
        ...config.globalCacheSettings,
        ...settings,
    };
    saveConfig(baseDir, config);
}
// ===========================
// CodexLens Embedding Rotation Management
// ===========================
/**
 * Get CodexLens embedding rotation config
 */
export function getCodexLensEmbeddingRotation(baseDir) {
    const config = loadLiteLLMApiConfig(baseDir);
    return config.codexlensEmbeddingRotation;
}
/**
 * Update CodexLens embedding rotation config
 * Also triggers sync to CodexLens settings.json
 */
export function updateCodexLensEmbeddingRotation(baseDir, rotationConfig) {
    const config = loadLiteLLMApiConfig(baseDir);
    if (rotationConfig) {
        config.codexlensEmbeddingRotation = rotationConfig;
    }
    else {
        delete config.codexlensEmbeddingRotation;
    }
    saveConfig(baseDir, config);
    // Auto-sync to CodexLens settings.json
    const syncResult = syncCodexLensConfig(baseDir);
    return { syncResult };
}
/**
 * Get all enabled embedding providers with their API keys for rotation
 * This aggregates all providers that have embedding models configured
 */
export function getEmbeddingProvidersForRotation(baseDir) {
    const config = loadLiteLLMApiConfig(baseDir);
    const result = [];
    for (const provider of config.providers) {
        if (!provider.enabled)
            continue;
        // Check if provider has embedding models
        const embeddingModels = (provider.embeddingModels || [])
            .filter(m => m.enabled)
            .map(m => ({
            modelId: m.id,
            modelName: m.name,
            dimensions: m.capabilities?.embeddingDimension || 1536,
        }));
        if (embeddingModels.length === 0)
            continue;
        // Get API keys (single key or multiple from apiKeys array)
        const apiKeys = [];
        if (provider.apiKeys && provider.apiKeys.length > 0) {
            // Use multi-key configuration
            for (const keyEntry of provider.apiKeys) {
                apiKeys.push({
                    keyId: keyEntry.id,
                    keyLabel: keyEntry.label || keyEntry.id,
                    enabled: keyEntry.enabled,
                });
            }
        }
        else if (provider.apiKey) {
            // Single key fallback
            apiKeys.push({
                keyId: 'default',
                keyLabel: 'Default Key',
                enabled: true,
            });
        }
        result.push({
            providerId: provider.id,
            providerName: provider.name,
            apiBase: provider.apiBase || getDefaultApiBaseForType(provider.type),
            embeddingModels,
            apiKeys,
        });
    }
    return result;
}
/**
 * Generate rotation endpoints for ccw_litellm
 * Creates endpoint list from rotation config for parallel embedding
 * Supports both legacy codexlensEmbeddingRotation and new embeddingPoolConfig
 */
export function generateRotationEndpoints(baseDir) {
    const config = loadLiteLLMApiConfig(baseDir);
    // Prefer embeddingPoolConfig, fallback to codexlensEmbeddingRotation for backward compatibility
    const poolConfig = config.embeddingPoolConfig;
    const rotationConfig = config.codexlensEmbeddingRotation;
    // Check if new poolConfig is enabled
    if (poolConfig && poolConfig.enabled) {
        return generateEndpointsFromPool(baseDir, poolConfig, config);
    }
    // Fallback to legacy rotation config
    if (rotationConfig && rotationConfig.enabled) {
        return generateEndpointsFromLegacyRotation(baseDir, rotationConfig, config);
    }
    return [];
}
/**
 * Generate endpoints from new embeddingPoolConfig (with auto-discovery support)
 */
function generateEndpointsFromPool(baseDir, poolConfig, config) {
    const endpoints = [];
    if (poolConfig.autoDiscover) {
        // Auto-discover all providers offering targetModel
        const discovered = discoverProvidersForModel(baseDir, poolConfig.targetModel);
        const excludedIds = new Set(poolConfig.excludedProviderIds || []);
        for (const disc of discovered) {
            // Skip excluded providers
            if (excludedIds.has(disc.providerId))
                continue;
            // Find the provider config
            const provider = config.providers.find(p => p.id === disc.providerId);
            if (!provider || !provider.enabled)
                continue;
            // Find the embedding model
            const embeddingModel = provider.embeddingModels?.find(m => m.id === disc.modelId);
            if (!embeddingModel || !embeddingModel.enabled)
                continue;
            // Get API base (model-specific or provider default)
            const apiBase = embeddingModel.endpointSettings?.baseUrl ||
                provider.apiBase ||
                getDefaultApiBaseForType(provider.type);
            // Get API keys to use
            let keysToUse = [];
            if (provider.apiKeys && provider.apiKeys.length > 0) {
                // Use all enabled and healthy keys (filter out unhealthy)
                const healthyKeys = provider.apiKeys.filter(k => k.enabled && k.healthStatus !== 'unhealthy');
                // Log filtered keys for debugging
                const unhealthyCount = provider.apiKeys.filter(k => k.enabled && k.healthStatus === 'unhealthy').length;
                if (unhealthyCount > 0) {
                    console.log(`[RotationEndpoints] Filtered ${unhealthyCount} unhealthy key(s) from provider ${provider.name}`);
                }
                keysToUse = healthyKeys.map(k => ({ id: k.id, key: k.key, label: k.label || k.id }));
            }
            else if (provider.apiKey) {
                // Single key fallback
                keysToUse = [{ id: 'default', key: provider.apiKey, label: 'Default' }];
            }
            // Create endpoint for each key
            for (const keyInfo of keysToUse) {
                const endpoint = {
                    name: `${provider.name}-${keyInfo.label}`,
                    api_key: resolveEnvVar(keyInfo.key),
                    api_base: apiBase,
                    model: embeddingModel.name,
                    weight: 1.0, // Default weight for auto-discovered providers
                    max_concurrent: poolConfig.defaultMaxConcurrentPerKey,
                };
                // Add routing strategy from provider config
                if (provider.routingStrategy) {
                    endpoint.routing_strategy = provider.routingStrategy;
                }
                // Add health check config from provider
                if (provider.healthCheck) {
                    endpoint.health_check = {
                        enabled: provider.healthCheck.enabled,
                        interval_seconds: provider.healthCheck.intervalSeconds,
                        cooldown_seconds: provider.healthCheck.cooldownSeconds,
                        failure_threshold: provider.healthCheck.failureThreshold,
                    };
                }
                // Add last latency if available from key entry
                const keyEntry = provider.apiKeys?.find(k => k.id === keyInfo.id);
                if (keyEntry && keyEntry.lastLatencyMs !== undefined) {
                    endpoint.last_latency_ms = keyEntry.lastLatencyMs;
                }
                endpoints.push(endpoint);
            }
        }
    }
    return endpoints;
}
/**
 * Generate endpoints from legacy codexlensEmbeddingRotation config
 */
function generateEndpointsFromLegacyRotation(baseDir, rotationConfig, config) {
    const endpoints = [];
    for (const rotationProvider of rotationConfig.providers) {
        if (!rotationProvider.enabled)
            continue;
        // Find the provider config
        const provider = config.providers.find(p => p.id === rotationProvider.providerId);
        if (!provider || !provider.enabled)
            continue;
        // Find the embedding model
        const embeddingModel = provider.embeddingModels?.find(m => m.id === rotationProvider.modelId);
        if (!embeddingModel || !embeddingModel.enabled)
            continue;
        // Get API base (model-specific or provider default)
        const apiBase = embeddingModel.endpointSettings?.baseUrl ||
            provider.apiBase ||
            getDefaultApiBaseForType(provider.type);
        // Get API keys to use
        let keysToUse = [];
        if (provider.apiKeys && provider.apiKeys.length > 0) {
            // Filter out unhealthy keys first
            const healthyKeys = provider.apiKeys.filter(k => k.enabled && k.healthStatus !== 'unhealthy');
            // Log filtered keys for debugging
            const unhealthyCount = provider.apiKeys.filter(k => k.enabled && k.healthStatus === 'unhealthy').length;
            if (unhealthyCount > 0) {
                console.log(`[RotationEndpoints] Filtered ${unhealthyCount} unhealthy key(s) from provider ${provider.name}`);
            }
            if (rotationProvider.useAllKeys) {
                // Use all enabled and healthy keys
                keysToUse = healthyKeys.map(k => ({ id: k.id, key: k.key, label: k.label || k.id }));
            }
            else if (rotationProvider.selectedKeyIds && rotationProvider.selectedKeyIds.length > 0) {
                // Use only selected healthy keys
                keysToUse = healthyKeys
                    .filter(k => rotationProvider.selectedKeyIds.includes(k.id))
                    .map(k => ({ id: k.id, key: k.key, label: k.label || k.id }));
            }
        }
        else if (provider.apiKey) {
            // Single key fallback
            keysToUse = [{ id: 'default', key: provider.apiKey, label: 'Default' }];
        }
        // Create endpoint for each key
        for (const keyInfo of keysToUse) {
            const endpoint = {
                name: `${provider.name}-${keyInfo.label}`,
                api_key: resolveEnvVar(keyInfo.key),
                api_base: apiBase,
                model: embeddingModel.name,
                weight: rotationProvider.weight,
                max_concurrent: rotationProvider.maxConcurrentPerKey,
            };
            // Add routing strategy from provider config
            if (provider.routingStrategy) {
                endpoint.routing_strategy = provider.routingStrategy;
            }
            // Add health check config from provider
            if (provider.healthCheck) {
                endpoint.health_check = {
                    enabled: provider.healthCheck.enabled,
                    interval_seconds: provider.healthCheck.intervalSeconds,
                    cooldown_seconds: provider.healthCheck.cooldownSeconds,
                    failure_threshold: provider.healthCheck.failureThreshold,
                };
            }
            // Add last latency if available from key entry
            const keyEntry = provider.apiKeys?.find(k => k.id === keyInfo.id);
            if (keyEntry && keyEntry.lastLatencyMs !== undefined) {
                endpoint.last_latency_ms = keyEntry.lastLatencyMs;
            }
            endpoints.push(endpoint);
        }
    }
    return endpoints;
}
/**
 * Sync CodexLens settings with CCW API config
 * Writes rotation endpoints to ~/.codexlens/settings.json
 * This enables the Python backend to use UI-configured rotation
 * Supports both new embeddingPoolConfig and legacy codexlensEmbeddingRotation
 */
export function syncCodexLensConfig(baseDir) {
    try {
        const config = loadLiteLLMApiConfig(baseDir);
        // Prefer embeddingPoolConfig, fallback to codexlensEmbeddingRotation
        const poolConfig = config.embeddingPoolConfig;
        const rotationConfig = config.codexlensEmbeddingRotation;
        // Get CodexLens settings path
        const codexlensDir = join(homedir(), '.codexlens');
        const settingsPath = join(codexlensDir, 'settings.json');
        // Ensure directory exists
        if (!existsSync(codexlensDir)) {
            mkdirSync(codexlensDir, { recursive: true });
        }
        // Load existing settings or create new
        let settings = {};
        if (existsSync(settingsPath)) {
            try {
                settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
            }
            catch {
                settings = {};
            }
        }
        // Check if either config is enabled
        const isPoolEnabled = poolConfig && poolConfig.enabled;
        const isRotationEnabled = rotationConfig && rotationConfig.enabled;
        // If neither is enabled, remove rotation endpoints and return
        if (!isPoolEnabled && !isRotationEnabled) {
            if (settings.litellm_rotation_endpoints) {
                delete settings.litellm_rotation_endpoints;
                delete settings.litellm_rotation_strategy;
                delete settings.litellm_rotation_cooldown;
                delete settings.litellm_target_model;
                writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
            }
            return { success: true, message: 'Rotation disabled, cleared endpoints', endpointCount: 0 };
        }
        // Generate rotation endpoints (function handles priority internally)
        const endpoints = generateRotationEndpoints(baseDir);
        if (endpoints.length === 0) {
            return { success: false, message: 'No valid endpoints generated from rotation config' };
        }
        // Update settings with rotation config (use poolConfig if available)
        settings.litellm_rotation_endpoints = endpoints;
        if (isPoolEnabled) {
            settings.litellm_rotation_strategy = poolConfig.strategy;
            settings.litellm_rotation_cooldown = poolConfig.defaultCooldown;
            settings.litellm_target_model = poolConfig.targetModel;
        }
        else {
            settings.litellm_rotation_strategy = rotationConfig.strategy;
            settings.litellm_rotation_cooldown = rotationConfig.defaultCooldown;
            settings.litellm_target_model = rotationConfig.targetModel;
        }
        // Write updated settings
        writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
        return {
            success: true,
            message: `Synced ${endpoints.length} rotation endpoints to CodexLens`,
            endpointCount: endpoints.length,
        };
    }
    catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        console.error('[LiteLLM Config] Failed to sync CodexLens config:', errorMessage);
        return { success: false, message: `Sync failed: ${errorMessage}` };
    }
}
// ===========================
// Embedding Pool Management (Generic, with Auto-Discovery)
// ===========================
/**
 * Get embedding pool config
 */
export function getEmbeddingPoolConfig(baseDir) {
    const config = loadLiteLLMApiConfig(baseDir);
    return config.embeddingPoolConfig;
}
/**
 * Update embedding pool config
 * Also triggers sync to CodexLens settings.json if enabled
 */
export function updateEmbeddingPoolConfig(baseDir, poolConfig) {
    const config = loadLiteLLMApiConfig(baseDir);
    if (poolConfig) {
        config.embeddingPoolConfig = poolConfig;
    }
    else {
        delete config.embeddingPoolConfig;
    }
    saveConfig(baseDir, config);
    // Auto-sync to CodexLens settings.json
    const syncResult = syncCodexLensConfig(baseDir);
    return { syncResult };
}
/**
 * Discover all providers that offer a specific embedding model
 * Returns list of {providerId, providerName, modelId, modelName, apiKeys[]}
 */
export function discoverProvidersForModel(baseDir, targetModel) {
    const config = loadLiteLLMApiConfig(baseDir);
    const result = [];
    for (const provider of config.providers) {
        if (!provider.enabled)
            continue;
        // Check if provider has embedding models matching targetModel
        const matchingModels = (provider.embeddingModels || []).filter(m => m.enabled && (m.id === targetModel || m.name === targetModel));
        if (matchingModels.length === 0)
            continue;
        // Get API keys (single key or multiple from apiKeys array)
        const apiKeys = [];
        if (provider.apiKeys && provider.apiKeys.length > 0) {
            // Use multi-key configuration
            for (const keyEntry of provider.apiKeys) {
                apiKeys.push({
                    keyId: keyEntry.id,
                    keyLabel: keyEntry.label || keyEntry.id,
                    enabled: keyEntry.enabled,
                });
            }
        }
        else if (provider.apiKey) {
            // Single key fallback
            apiKeys.push({
                keyId: 'default',
                keyLabel: 'Default Key',
                enabled: true,
            });
        }
        // Add each matching model
        for (const model of matchingModels) {
            result.push({
                providerId: provider.id,
                providerName: provider.name,
                modelId: model.id,
                modelName: model.name,
                apiKeys,
            });
        }
    }
    return result;
}
// ===========================
// YAML Config Generation for ccw_litellm
// ===========================
/**
 * Convert UI config (JSON) to ccw_litellm config (YAML format object)
 * This allows CodexLens to use UI-configured providers
 */
export function generateLiteLLMYamlConfig(baseDir) {
    const config = loadLiteLLMApiConfig(baseDir);
    // Build providers object
    const providers = {};
    for (const provider of config.providers) {
        if (!provider.enabled)
            continue;
        providers[provider.id] = {
            api_key: provider.apiKey,
            api_base: provider.apiBase || getDefaultApiBaseForType(provider.type),
        };
    }
    // Build embedding_models object from providers' embeddingModels
    const embeddingModels = {};
    for (const provider of config.providers) {
        if (!provider.enabled || !provider.embeddingModels)
            continue;
        for (const model of provider.embeddingModels) {
            if (!model.enabled)
                continue;
            embeddingModels[model.id] = {
                provider: provider.id,
                model: model.name,
                dimensions: model.capabilities?.embeddingDimension || 1536,
                // Use model-specific base URL if set, otherwise use provider's
                ...(model.endpointSettings?.baseUrl && {
                    api_base: model.endpointSettings.baseUrl,
                }),
            };
        }
    }
    // Build llm_models object from providers' llmModels
    const llmModels = {};
    for (const provider of config.providers) {
        if (!provider.enabled || !provider.llmModels)
            continue;
        for (const model of provider.llmModels) {
            if (!model.enabled)
                continue;
            llmModels[model.id] = {
                provider: provider.id,
                model: model.name,
                ...(model.endpointSettings?.baseUrl && {
                    api_base: model.endpointSettings.baseUrl,
                }),
            };
        }
    }
    // Find default provider
    const defaultProvider = config.providers.find((p) => p.enabled)?.id || 'openai';
    return {
        version: 1,
        default_provider: defaultProvider,
        providers,
        embedding_models: Object.keys(embeddingModels).length > 0 ? embeddingModels : {
            default: {
                provider: defaultProvider,
                model: 'text-embedding-3-small',
                dimensions: 1536,
            },
        },
        llm_models: Object.keys(llmModels).length > 0 ? llmModels : {
            default: {
                provider: defaultProvider,
                model: 'gpt-4',
            },
        },
    };
}
/**
 * Get default API base URL for provider type
 */
function getDefaultApiBaseForType(type) {
    const defaults = {
        openai: 'https://api.openai.com/v1',
        anthropic: 'https://api.anthropic.com/v1',
        custom: 'https://api.example.com/v1',
    };
    return defaults[type] || 'https://api.openai.com/v1';
}
/**
 * Save ccw_litellm YAML config file
 * Writes to ~/.ccw/config/litellm-config.yaml
 */
export function saveLiteLLMYamlConfig(baseDir) {
    const yamlConfig = generateLiteLLMYamlConfig(baseDir);
    // Convert to YAML manually (simple format)
    const yamlContent = objectToYaml(yamlConfig);
    // Write to ~/.ccw/config/litellm-config.yaml
    const homePath = process.env.HOME || process.env.USERPROFILE || '';
    const yamlPath = join(homePath, '.ccw', 'config', 'litellm-config.yaml');
    // Ensure directory exists
    const configDir = join(homePath, '.ccw', 'config');
    ensureStorageDir(configDir);
    writeFileSync(yamlPath, yamlContent, 'utf-8');
    return yamlPath;
}
/**
 * Simple object to YAML converter
 */
function objectToYaml(obj, indent = 0) {
    const spaces = '  '.repeat(indent);
    if (obj === null || obj === undefined) {
        return 'null';
    }
    if (typeof obj === 'string') {
        // Quote strings that contain special characters
        if (obj.includes(':') || obj.includes('#') || obj.includes('\n') || obj.startsWith('$')) {
            return `"${obj.replace(/"/g, '\\"')}"`;
        }
        return obj;
    }
    if (typeof obj === 'number' || typeof obj === 'boolean') {
        return String(obj);
    }
    if (Array.isArray(obj)) {
        if (obj.length === 0)
            return '[]';
        return obj.map((item) => `${spaces}- ${objectToYaml(item, indent + 1).trimStart()}`).join('\n');
    }
    if (typeof obj === 'object') {
        const entries = Object.entries(obj);
        if (entries.length === 0)
            return '{}';
        return entries
            .map(([key, value]) => {
            if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
                return `${spaces}${key}:\n${objectToYaml(value, indent + 1)}`;
            }
            return `${spaces}${key}: ${objectToYaml(value, indent)}`;
        })
            .join('\n');
    }
    return String(obj);
}
// ===========================
// Multi-Model Pool Management
// ===========================
/**
 * Migrate legacy embeddingPoolConfig to new modelPools array
 * This function ensures backward compatibility with existing configurations
 */
function migrateEmbeddingPoolToModelPools(config) {
    // Skip if already has modelPools or no legacy config
    if (config.modelPools && config.modelPools.length > 0)
        return;
    if (!config.embeddingPoolConfig)
        return;
    // Convert legacy embeddingPoolConfig to ModelPoolConfig
    const legacyPool = config.embeddingPoolConfig;
    const modelPool = {
        id: `pool-embedding-${Date.now()}`,
        modelType: 'embedding',
        enabled: legacyPool.enabled,
        targetModel: legacyPool.targetModel,
        strategy: legacyPool.strategy,
        autoDiscover: legacyPool.autoDiscover,
        excludedProviderIds: legacyPool.excludedProviderIds || [],
        defaultCooldown: legacyPool.defaultCooldown,
        defaultMaxConcurrentPerKey: legacyPool.defaultMaxConcurrentPerKey,
        name: `Embedding Pool - ${legacyPool.targetModel}`,
        description: 'Migrated from legacy embeddingPoolConfig',
    };
    config.modelPools = [modelPool];
    // Keep legacy config for backward compatibility with old CodexLens versions
}
/**
 * Get all model pool configurations
 * Returns empty array if no pools configured
 */
export function getModelPools(baseDir) {
    const config = loadLiteLLMApiConfig(baseDir);
    // Auto-migrate if needed
    migrateEmbeddingPoolToModelPools(config);
    return config.modelPools || [];
}
/**
 * Get a specific model pool by ID
 */
export function getModelPool(baseDir, poolId) {
    const pools = getModelPools(baseDir);
    return pools.find(p => p.id === poolId);
}
/**
 * Add a new model pool configuration
 */
export function addModelPool(baseDir, poolConfig) {
    const config = loadLiteLLMApiConfig(baseDir);
    // Auto-migrate if needed
    migrateEmbeddingPoolToModelPools(config);
    // Ensure modelPools array exists
    if (!config.modelPools) {
        config.modelPools = [];
    }
    // Generate unique ID
    const poolId = `pool-${poolConfig.modelType}-${Date.now()}`;
    const newPool = {
        ...poolConfig,
        id: poolId,
    };
    config.modelPools.push(newPool);
    saveConfig(baseDir, config);
    // Sync to CodexLens if this is an embedding pool
    const syncResult = poolConfig.modelType === 'embedding' && poolConfig.enabled
        ? syncCodexLensConfig(baseDir)
        : undefined;
    return { poolId, syncResult };
}
/**
 * Update an existing model pool configuration
 */
export function updateModelPool(baseDir, poolId, updates) {
    const config = loadLiteLLMApiConfig(baseDir);
    // Auto-migrate if needed
    migrateEmbeddingPoolToModelPools(config);
    if (!config.modelPools) {
        return { success: false };
    }
    const poolIndex = config.modelPools.findIndex(p => p.id === poolId);
    if (poolIndex === -1) {
        return { success: false };
    }
    // Apply updates
    config.modelPools[poolIndex] = {
        ...config.modelPools[poolIndex],
        ...updates,
    };
    saveConfig(baseDir, config);
    // Sync to CodexLens if this is an enabled embedding pool
    const pool = config.modelPools[poolIndex];
    const syncResult = pool.modelType === 'embedding' && pool.enabled
        ? syncCodexLensConfig(baseDir)
        : undefined;
    return { success: true, syncResult };
}
/**
 * Delete a model pool configuration
 */
export function deleteModelPool(baseDir, poolId) {
    const config = loadLiteLLMApiConfig(baseDir);
    if (!config.modelPools) {
        return { success: false };
    }
    const poolIndex = config.modelPools.findIndex(p => p.id === poolId);
    if (poolIndex === -1) {
        return { success: false };
    }
    const deletedPool = config.modelPools[poolIndex];
    config.modelPools.splice(poolIndex, 1);
    saveConfig(baseDir, config);
    // Sync to CodexLens if we deleted an embedding pool
    const syncResult = deletedPool.modelType === 'embedding'
        ? syncCodexLensConfig(baseDir)
        : undefined;
    return { success: true, syncResult };
}
/**
 * Get available models for a specific model type
 * Used for pool configuration UI
 */
export function getAvailableModelsForType(baseDir, modelType) {
    const config = loadLiteLLMApiConfig(baseDir);
    const availableModels = [];
    const modelMap = new Map();
    for (const provider of config.providers) {
        if (!provider.enabled)
            continue;
        let models;
        switch (modelType) {
            case 'embedding':
                models = provider.embeddingModels;
                break;
            case 'llm':
                models = provider.llmModels;
                break;
            case 'reranker':
                models = provider.rerankerModels;
                break;
        }
        if (!models)
            continue;
        for (const model of models) {
            if (!model.enabled)
                continue;
            const key = model.id;
            if (modelMap.has(key)) {
                modelMap.get(key).providers.push(provider.name);
            }
            else {
                modelMap.set(key, {
                    modelId: model.id,
                    modelName: model.name,
                    providers: [provider.name],
                });
            }
        }
    }
    availableModels.push(...Array.from(modelMap.values()));
    return availableModels;
}
//# sourceMappingURL=litellm-api-config-manager.js.map