/**
 * TOML Configuration System
 *
 * Hierarchical configuration using TOML format (mistral-vibe style).
 * Supports providers, models, tool configs, and user preferences.
 */
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
import { homedir } from 'os';
import { join, dirname } from 'path';
import { logger } from '../utils/logger.js';
// ============================================================================
// Default Configuration
// ============================================================================
export const DEFAULT_CONFIG = {
    active_model: 'grok-code-fast',
    providers: {
        xai: {
            base_url: 'https://api.x.ai/v1',
            api_key_env: 'GROK_API_KEY',
            type: 'xai',
            enabled: true,
        },
        anthropic: {
            base_url: 'https://api.anthropic.com/v1',
            api_key_env: 'ANTHROPIC_API_KEY',
            type: 'anthropic',
            enabled: true,
        },
        openai: {
            base_url: 'https://api.openai.com/v1',
            api_key_env: 'OPENAI_API_KEY',
            type: 'openai',
            enabled: true,
        },
        google: {
            base_url: 'https://generativelanguage.googleapis.com/v1beta',
            api_key_env: 'GOOGLE_API_KEY',
            type: 'google',
            enabled: true,
        },
    },
    models: {
        'grok-code-fast': {
            provider: 'xai',
            model_id: 'grok-3-fast-latest',
            price_per_m_input: 5.0,
            price_per_m_output: 15.0,
            max_context_tokens: 131072,
            description: 'Fast Grok model optimized for code',
        },
        'grok-3': {
            provider: 'xai',
            model_id: 'grok-3-latest',
            price_per_m_input: 3.0,
            price_per_m_output: 15.0,
            max_context_tokens: 131072,
            description: 'Full Grok 3 model',
        },
        'claude-sonnet': {
            provider: 'anthropic',
            model_id: 'claude-sonnet-4-20250514',
            price_per_m_input: 3.0,
            price_per_m_output: 15.0,
            max_context_tokens: 200000,
            description: 'Claude Sonnet 4',
        },
        'claude-opus': {
            provider: 'anthropic',
            model_id: 'claude-opus-4-20250514',
            price_per_m_input: 15.0,
            price_per_m_output: 75.0,
            max_context_tokens: 200000,
            description: 'Claude Opus 4',
        },
        'gpt-4o': {
            provider: 'openai',
            model_id: 'gpt-4o',
            price_per_m_input: 2.5,
            price_per_m_output: 10.0,
            max_context_tokens: 128000,
            description: 'GPT-4o',
        },
        'gemini-2': {
            provider: 'google',
            model_id: 'gemini-2.0-flash-exp',
            price_per_m_input: 0.0,
            price_per_m_output: 0.0,
            max_context_tokens: 1000000,
            description: 'Gemini 2.0 Flash (free tier)',
        },
    },
    tool_config: {
        bash: {
            permission: 'ask',
            timeout: 120,
            allowlist: [
                'git .*',
                'npm .*',
                'npx .*',
                'yarn .*',
                'pnpm .*',
                'cargo .*',
                'python .*',
                'node .*',
                'ls.*',
                'cat.*',
                'head.*',
                'tail.*',
                'grep.*',
                'find.*',
                'which.*',
                'pwd',
                'echo.*',
            ],
            denylist: [
                'rm -rf /',
                'rm -rf ~',
                'rm -rf \\*',
                'sudo .*',
                'chmod 777.*',
                'curl.*\\| ?sh',
                'wget.*\\| ?sh',
                ':(){ :\\|:& };:',
            ],
        },
        str_replace_editor: {
            permission: 'ask',
            timeout: 30,
        },
        create_file: {
            permission: 'ask',
            timeout: 10,
        },
        view_file: {
            permission: 'always',
            timeout: 10,
        },
        search: {
            permission: 'always',
            timeout: 30,
        },
        web_search: {
            permission: 'always',
            timeout: 30,
        },
        web_fetch: {
            permission: 'ask',
            timeout: 60,
        },
    },
    middleware: {
        max_turns: 100,
        turn_warning_threshold: 0.8,
        max_cost: 10.0,
        cost_warning_threshold: 0.8,
        auto_compact_threshold: 80000,
        context_warning_percentage: 0.7,
    },
    ui: {
        vim_keybindings: false,
        theme: 'default',
        show_tokens: true,
        show_cost: true,
        streaming: true,
        sound_effects: false,
    },
    agent: {
        yolo_mode: false,
        parallel_tools: false,
        rag_tool_selection: true,
        self_healing: true,
        default_prompt: 'default',
    },
};
// ============================================================================
// TOML Parser/Serializer (Simple Implementation)
// ============================================================================
/**
 * Simple TOML parser for our config format
 * Note: This is a minimal implementation. For full TOML support, consider using a library.
 */
export function parseTOML(content) {
    const result = {};
    const lines = content.split('\n');
    let currentSection = '';
    let currentSubSection = '';
    for (let line of lines) {
        line = line.trim();
        // Skip empty lines and comments
        if (!line || line.startsWith('#'))
            continue;
        // Section header [section] or [section.subsection]
        const sectionMatch = line.match(/^\[([^\]]+)\]$/);
        if (sectionMatch) {
            const parts = sectionMatch[1].split('.');
            currentSection = parts[0];
            currentSubSection = parts.slice(1).join('.');
            // Initialize section if needed
            if (!result[currentSection]) {
                result[currentSection] = {};
            }
            if (currentSubSection) {
                const sectionObj = result[currentSection];
                if (!sectionObj[currentSubSection]) {
                    sectionObj[currentSubSection] = {};
                }
            }
            continue;
        }
        // Key-value pair
        const kvMatch = line.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/);
        if (kvMatch) {
            const key = kvMatch[1];
            let value = kvMatch[2].trim();
            // Parse value type
            if (value === 'true') {
                value = true;
            }
            else if (value === 'false') {
                value = false;
            }
            else if (typeof value === 'string' && /^-?\d+$/.test(value)) {
                value = parseInt(value, 10);
            }
            else if (typeof value === 'string' && /^-?\d+\.\d+$/.test(value)) {
                value = parseFloat(value);
            }
            else if (typeof value === 'string' && value.startsWith('"') && value.endsWith('"')) {
                value = value.slice(1, -1);
            }
            else if (typeof value === 'string' && value.startsWith('[') && value.endsWith(']')) {
                // Parse array
                const arrayContent = value.slice(1, -1);
                value = arrayContent.split(',').map(item => {
                    item = item.trim();
                    if (item.startsWith('"') && item.endsWith('"')) {
                        return item.slice(1, -1);
                    }
                    return item;
                }).filter(item => item !== '');
            }
            // Store value
            if (currentSubSection) {
                const sectionObj = result[currentSection];
                const subSectionObj = sectionObj[currentSubSection];
                subSectionObj[key] = value;
            }
            else if (currentSection) {
                result[currentSection][key] = value;
            }
            else {
                result[key] = value;
            }
        }
    }
    return result;
}
/**
 * Serialize config to TOML format
 */
export function serializeTOML(config) {
    const lines = [
        '# Code Buddy Configuration',
        '# See https://github.com/phuetz/code-buddy for documentation',
        '',
    ];
    // Root level
    lines.push(`active_model = "${config.active_model}"`);
    lines.push('');
    // Providers
    for (const [name, provider] of Object.entries(config.providers)) {
        lines.push(`[providers.${name}]`);
        if (provider.base_url)
            lines.push(`base_url = "${provider.base_url}"`);
        lines.push(`api_key_env = "${provider.api_key_env}"`);
        lines.push(`type = "${provider.type}"`);
        if (provider.enabled !== undefined)
            lines.push(`enabled = ${provider.enabled}`);
        lines.push('');
    }
    // Models
    for (const [name, model] of Object.entries(config.models)) {
        lines.push(`[models.${name}]`);
        lines.push(`provider = "${model.provider}"`);
        if (model.model_id)
            lines.push(`model_id = "${model.model_id}"`);
        lines.push(`price_per_m_input = ${model.price_per_m_input}`);
        lines.push(`price_per_m_output = ${model.price_per_m_output}`);
        lines.push(`max_context_tokens = ${model.max_context_tokens}`);
        if (model.description)
            lines.push(`description = "${model.description}"`);
        lines.push('');
    }
    // Tool config
    for (const [name, tool] of Object.entries(config.tool_config)) {
        lines.push(`[tool_config.${name}]`);
        lines.push(`permission = "${tool.permission}"`);
        if (tool.timeout)
            lines.push(`timeout = ${tool.timeout}`);
        if (tool.allowlist?.length) {
            lines.push(`allowlist = [${tool.allowlist.map(p => `"${p}"`).join(', ')}]`);
        }
        if (tool.denylist?.length) {
            lines.push(`denylist = [${tool.denylist.map(p => `"${p}"`).join(', ')}]`);
        }
        lines.push('');
    }
    // Middleware
    lines.push('[middleware]');
    if (config.middleware.max_turns)
        lines.push(`max_turns = ${config.middleware.max_turns}`);
    if (config.middleware.turn_warning_threshold)
        lines.push(`turn_warning_threshold = ${config.middleware.turn_warning_threshold}`);
    if (config.middleware.max_cost)
        lines.push(`max_cost = ${config.middleware.max_cost}`);
    if (config.middleware.cost_warning_threshold)
        lines.push(`cost_warning_threshold = ${config.middleware.cost_warning_threshold}`);
    if (config.middleware.auto_compact_threshold)
        lines.push(`auto_compact_threshold = ${config.middleware.auto_compact_threshold}`);
    if (config.middleware.context_warning_percentage)
        lines.push(`context_warning_percentage = ${config.middleware.context_warning_percentage}`);
    lines.push('');
    // UI
    lines.push('[ui]');
    if (config.ui.vim_keybindings !== undefined)
        lines.push(`vim_keybindings = ${config.ui.vim_keybindings}`);
    if (config.ui.theme)
        lines.push(`theme = "${config.ui.theme}"`);
    if (config.ui.show_tokens !== undefined)
        lines.push(`show_tokens = ${config.ui.show_tokens}`);
    if (config.ui.show_cost !== undefined)
        lines.push(`show_cost = ${config.ui.show_cost}`);
    if (config.ui.streaming !== undefined)
        lines.push(`streaming = ${config.ui.streaming}`);
    if (config.ui.sound_effects !== undefined)
        lines.push(`sound_effects = ${config.ui.sound_effects}`);
    lines.push('');
    // Agent
    lines.push('[agent]');
    if (config.agent.yolo_mode !== undefined)
        lines.push(`yolo_mode = ${config.agent.yolo_mode}`);
    if (config.agent.parallel_tools !== undefined)
        lines.push(`parallel_tools = ${config.agent.parallel_tools}`);
    if (config.agent.rag_tool_selection !== undefined)
        lines.push(`rag_tool_selection = ${config.agent.rag_tool_selection}`);
    if (config.agent.self_healing !== undefined)
        lines.push(`self_healing = ${config.agent.self_healing}`);
    if (config.agent.default_prompt)
        lines.push(`default_prompt = "${config.agent.default_prompt}"`);
    lines.push('');
    return lines.join('\n');
}
// ============================================================================
// Configuration Manager
// ============================================================================
const CONFIG_DIR = join(homedir(), '.codebuddy');
const CONFIG_FILE = join(CONFIG_DIR, 'config.toml');
const PROJECT_CONFIG_FILE = '.codebuddy/config.toml';
/**
 * Configuration manager singleton
 */
class ConfigManager {
    config;
    loaded = false;
    constructor() {
        this.config = { ...DEFAULT_CONFIG };
    }
    /**
     * Load configuration from files
     * Priority: project > user > defaults
     */
    load() {
        if (this.loaded)
            return this.config;
        // Start with defaults
        this.config = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
        // Load user config
        if (existsSync(CONFIG_FILE)) {
            try {
                const content = readFileSync(CONFIG_FILE, 'utf-8');
                const userConfig = parseTOML(content);
                this.mergeConfig(userConfig);
            }
            catch (error) {
                logger.warn(`Warning: Failed to parse user config: ${error}`, { source: 'ConfigManager' });
            }
        }
        // Load project config (overrides user config)
        if (existsSync(PROJECT_CONFIG_FILE)) {
            try {
                const content = readFileSync(PROJECT_CONFIG_FILE, 'utf-8');
                const projectConfig = parseTOML(content);
                this.mergeConfig(projectConfig);
            }
            catch (error) {
                logger.warn(`Warning: Failed to parse project config: ${error}`, { source: 'ConfigManager' });
            }
        }
        this.loaded = true;
        return this.config;
    }
    /**
     * Deep merge config
     */
    mergeConfig(partial) {
        if (partial.active_model) {
            this.config.active_model = partial.active_model;
        }
        if (partial.providers) {
            this.config.providers = { ...this.config.providers, ...partial.providers };
        }
        if (partial.models) {
            this.config.models = { ...this.config.models, ...partial.models };
        }
        if (partial.tool_config) {
            for (const [name, toolConfig] of Object.entries(partial.tool_config)) {
                this.config.tool_config[name] = {
                    ...this.config.tool_config[name],
                    ...toolConfig,
                };
            }
        }
        if (partial.middleware) {
            this.config.middleware = { ...this.config.middleware, ...partial.middleware };
        }
        if (partial.ui) {
            this.config.ui = { ...this.config.ui, ...partial.ui };
        }
        if (partial.agent) {
            this.config.agent = { ...this.config.agent, ...partial.agent };
        }
    }
    /**
     * Get current config
     */
    getConfig() {
        if (!this.loaded)
            this.load();
        return this.config;
    }
    /**
     * Get active model config
     */
    getActiveModel() {
        const config = this.getConfig();
        const model = config.models[config.active_model];
        if (!model) {
            throw new Error(`Model "${config.active_model}" not found in config`);
        }
        return { ...model, name: config.active_model };
    }
    /**
     * Get provider config for a model
     */
    getProviderForModel(modelName) {
        const config = this.getConfig();
        const model = config.models[modelName];
        if (!model) {
            throw new Error(`Model "${modelName}" not found in config`);
        }
        const provider = config.providers[model.provider];
        if (!provider) {
            throw new Error(`Provider "${model.provider}" not found in config`);
        }
        return { ...provider, name: model.provider };
    }
    /**
     * Get tool config
     */
    getToolConfig(toolName) {
        return this.getConfig().tool_config[toolName];
    }
    /**
     * Check if tool command is allowed
     */
    isToolCommandAllowed(toolName, command) {
        const toolConfig = this.getToolConfig(toolName);
        if (!toolConfig)
            return { allowed: true };
        // Check denylist first
        if (toolConfig.denylist?.length) {
            for (const pattern of toolConfig.denylist) {
                try {
                    const regex = new RegExp(pattern);
                    if (regex.test(command)) {
                        return { allowed: false, reason: `Blocked by pattern: ${pattern}` };
                    }
                }
                catch {
                    // Invalid regex, skip
                }
            }
        }
        // Check allowlist (if defined, command must match)
        if (toolConfig.allowlist?.length) {
            for (const pattern of toolConfig.allowlist) {
                try {
                    const regex = new RegExp(`^${pattern}$`);
                    if (regex.test(command)) {
                        return { allowed: true };
                    }
                }
                catch {
                    // Invalid regex, skip
                }
            }
            return { allowed: false, reason: 'Command not in allowlist' };
        }
        return { allowed: true };
    }
    /**
     * Save user config
     */
    saveUserConfig() {
        const dir = dirname(CONFIG_FILE);
        if (!existsSync(dir)) {
            mkdirSync(dir, { recursive: true });
        }
        writeFileSync(CONFIG_FILE, serializeTOML(this.config));
    }
    /**
     * Set active model
     */
    setActiveModel(modelName) {
        if (!this.config.models[modelName]) {
            throw new Error(`Model "${modelName}" not found`);
        }
        this.config.active_model = modelName;
    }
    /**
     * Reload configuration
     */
    reload() {
        this.loaded = false;
        return this.load();
    }
    /**
     * Get config file path
     */
    getConfigPath() {
        return CONFIG_FILE;
    }
    /**
     * Check if config file exists
     */
    configExists() {
        return existsSync(CONFIG_FILE);
    }
    /**
     * Initialize config file with defaults
     */
    initConfig() {
        if (!existsSync(CONFIG_DIR)) {
            mkdirSync(CONFIG_DIR, { recursive: true });
        }
        if (!existsSync(CONFIG_FILE)) {
            writeFileSync(CONFIG_FILE, serializeTOML(DEFAULT_CONFIG));
        }
    }
}
// ============================================================================
// Singleton Export
// ============================================================================
let configManagerInstance = null;
export function getConfigManager() {
    if (!configManagerInstance) {
        configManagerInstance = new ConfigManager();
    }
    return configManagerInstance;
}
//# sourceMappingURL=toml-config.js.map