"use strict";
// src/cli/ConfigTui.ts
// OpenLife configuration TUI — Matrix 2026 theme.
//
// Sections:
//   1. Provider & Model       (selector + inline reauth)
//   2. API Keys               (masked editor for OPENAI/ANTHROPIC/GEMINI/OPENROUTER)
//   3. Telegram Bot           (token + allowed user + getMe validation)
//   4. Voice / TTS            (enabled flag + provider)
//   5. Daemon                 (start / stop / restart shortcuts)
//
// Used by `openlife config` and `/config` slash in ChatTui.
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 () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.runConfig = runConfig;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const readline = __importStar(require("readline"));
const child_process_1 = require("child_process");
const MatrixTheme_1 = require("./MatrixTheme");
const ProviderSelector_1 = require("./ProviderSelector");
const InstallModules_1 = require("./InstallModules");
const PROVIDERS = [
    { value: 'openai-cli', label: 'OpenAI Codex (gpt-5.5 via OAuth)', defaultModel: 'gpt-5.5', authCommand: 'codex logout && codex login' },
    { value: 'openai-api', label: 'OpenAI API (gpt-5.4-mini)', defaultModel: 'gpt-5.4-mini', authCommand: 'set OPENAI_API_KEY in .env' },
    { value: 'anthropic', label: 'Anthropic (Claude)', defaultModel: 'claude-opus-4-7', authCommand: 'set ANTHROPIC_API_KEY in .env' },
    { value: 'gemini-api', label: 'Google Gemini API', defaultModel: 'gemini-3.1-flash', authCommand: 'set GEMINI_API_KEY in .env' },
    { value: 'gemini-cli', label: 'Google Gemini CLI (OAuth)', defaultModel: 'gemini-3.1-pro', authCommand: 'gemini logout && gemini login --device-auth' },
    { value: 'openrouter', label: 'OpenRouter (100+ models)', defaultModel: 'openai/gpt-4.1', authCommand: 'set OPENROUTER_API_KEY in .env' },
    { value: 'ollama', label: 'Ollama (local)', defaultModel: 'qwen2.5-coder:7b', authCommand: 'set OLLAMA_URL in .env' },
];
function readModelsJson(root) {
    const p = path.join(root, 'models.json');
    if (!fs.existsSync(p))
        return {};
    try {
        return JSON.parse(fs.readFileSync(p, 'utf-8'));
    }
    catch {
        return {};
    }
}
function writeModelsJson(root, content) {
    fs.writeFileSync(path.join(root, 'models.json'), JSON.stringify(content, null, 2) + '\n', 'utf-8');
}
function readEnv(root) {
    const out = {};
    const p = path.join(root, '.env');
    if (!fs.existsSync(p))
        return out;
    for (const line of fs.readFileSync(p, 'utf-8').split('\n')) {
        const eq = line.indexOf('=');
        if (eq < 1)
            continue;
        out[line.slice(0, eq)] = line.slice(eq + 1).replace(/^['"]|['"]$/g, '');
    }
    return out;
}
function mask(s) {
    if (!s)
        return '(unset)';
    if (s.length <= 6)
        return '***';
    return s.slice(0, 4) + '…' + s.slice(-2);
}
function hr(width = 60) {
    process.stdout.write((0, MatrixTheme_1.paint)('─'.repeat(width), MatrixTheme_1.MATRIX.tail) + '\n');
}
async function ask(question, opts = {}) {
    const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
    return new Promise((resolve) => {
        if (opts.hidden) {
            const stdin = process.stdin;
            const onData = (char) => {
                const c = char.toString();
                if (c === '\n' || c === '\r' || c === '')
                    stdin.removeListener('data', onData);
            };
            stdin.on('data', onData);
        }
        rl.question(`${question} `, (answer) => {
            rl.close();
            process.stdout.write('\n');
            resolve(answer.trim());
        });
    });
}
// ─── Section: Provider & Model ───────────────────────────────────────
async function sectionProvider(root) {
    const current = readModelsJson(root);
    const activeProvider = current.primary?.provider || 'openai-cli';
    const activeRaw = current.primary?.raw || 'openai-cli/gpt-5.5';
    process.stdout.write('\n');
    const chosen = await (0, ProviderSelector_1.selectOne)({
        title: (0, MatrixTheme_1.paint)('Select provider', MatrixTheme_1.MATRIX.head),
        active: activeProvider,
        options: PROVIDERS.map((p) => ({ value: p.value, label: p.label, hint: p.value === activeProvider ? '' : '' })),
    });
    if (!chosen) {
        process.stdout.write((0, MatrixTheme_1.paint)('cancelled\n', MatrixTheme_1.MATRIX.tail));
        return;
    }
    const row = PROVIDERS.find((p) => p.value === chosen);
    if (!row)
        return;
    // Build the new ModelIdentifier (provider + default name)
    const newRaw = `${row.value}/${row.defaultModel}`;
    const wasActive = newRaw === activeRaw;
    hr();
    process.stdout.write(`Active model: ${(0, MatrixTheme_1.paint)(activeRaw, MatrixTheme_1.MATRIX.head)}\n`);
    process.stdout.write(`Selected:     ${(0, MatrixTheme_1.paint)(newRaw, MatrixTheme_1.MATRIX.body)}${wasActive ? (0, MatrixTheme_1.paint)('  (already active)', MatrixTheme_1.MATRIX.tail) : ''}\n`);
    process.stdout.write(`Auth path:    ${(0, MatrixTheme_1.paint)(row.authCommand, MatrixTheme_1.MATRIX.tail)}\n`);
    const action = await (0, ProviderSelector_1.numberedChoice)('What now?', [
        { key: '1', label: wasActive ? 'Keep current credentials' : `Switch primary to ${newRaw}` },
        { key: '2', label: `Reauthenticate (${row.authCommand})` },
        { key: '3', label: 'Cancel' },
    ]);
    if (action === '3' || !action) {
        process.stdout.write((0, MatrixTheme_1.paint)('cancelled\n', MatrixTheme_1.MATRIX.tail));
        return;
    }
    if (action === '1' && !wasActive) {
        const next = {
            primary: { provider: row.value, name: row.defaultModel, raw: newRaw },
            fallbacks: current.fallbacks ?? [],
        };
        writeModelsJson(root, next);
        process.stdout.write((0, MatrixTheme_1.paint)(`✓ primary model updated to ${newRaw}\n`, MatrixTheme_1.MATRIX.head));
    }
    if (action === '2') {
        if (row.value === 'openai-cli') {
            process.stdout.write((0, MatrixTheme_1.paint)('Running codex logout && codex login (interactive)…\n', MatrixTheme_1.MATRIX.body));
            try {
                (0, child_process_1.execFileSync)('codex', ['logout'], { stdio: 'inherit' });
            }
            catch { /* logout may fail if already out */ }
            try {
                (0, child_process_1.execFileSync)('codex', ['login'], { stdio: 'inherit' });
                process.stdout.write((0, MatrixTheme_1.paint)('✓ codex login complete\n', MatrixTheme_1.MATRIX.head));
            }
            catch (e) {
                process.stdout.write((0, MatrixTheme_1.paint)(`✗ codex login failed: ${e instanceof Error ? e.message : String(e)}\n`, MatrixTheme_1.MATRIX.err));
            }
        }
        else if (row.value === 'gemini-cli') {
            try {
                (0, child_process_1.execFileSync)('gemini', ['logout'], { stdio: 'inherit' });
            }
            catch { /* ignore */ }
            try {
                (0, child_process_1.execFileSync)('gemini', ['login', '--device-auth'], { stdio: 'inherit' });
                process.stdout.write((0, MatrixTheme_1.paint)('✓ gemini login complete\n', MatrixTheme_1.MATRIX.head));
            }
            catch (e) {
                process.stdout.write((0, MatrixTheme_1.paint)(`✗ gemini login failed: ${e instanceof Error ? e.message : String(e)}\n`, MatrixTheme_1.MATRIX.err));
            }
        }
        else {
            // API-key providers — open the keys editor scoped to this provider
            const keyName = {
                'openai-api': 'OPENAI_API_KEY',
                'anthropic': 'ANTHROPIC_API_KEY',
                'gemini-api': 'GEMINI_API_KEY',
                'openrouter': 'OPENROUTER_API_KEY',
                'ollama': 'OLLAMA_URL',
            }[row.value];
            process.stdout.write(`Paste new ${keyName} (input visible — clear the screen after):\n`);
            const value = await ask(`${keyName}:`);
            if (value) {
                if (keyName.endsWith('_API_KEY')) {
                    const slot = keyName.replace('_API_KEY', '').toLowerCase();
                    (0, InstallModules_1.saveApiKeysToEnv)(root, { [slot]: value });
                    process.stdout.write((0, MatrixTheme_1.paint)(`✓ ${keyName} saved to .env\n`, MatrixTheme_1.MATRIX.head));
                }
                else if (keyName === 'OLLAMA_URL') {
                    // Manual .env edit
                    const envPath = path.join(root, '.env');
                    const cur = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf-8') : '';
                    const lines = cur.split('\n').filter(Boolean).filter((l) => !l.startsWith('OLLAMA_URL='));
                    lines.push(`OLLAMA_URL=${value}`);
                    fs.writeFileSync(envPath, lines.join('\n') + '\n', 'utf-8');
                    process.stdout.write((0, MatrixTheme_1.paint)('✓ OLLAMA_URL saved\n', MatrixTheme_1.MATRIX.head));
                }
            }
            else {
                process.stdout.write((0, MatrixTheme_1.paint)('(empty — kept current)\n', MatrixTheme_1.MATRIX.tail));
            }
        }
    }
    // If user chose action=1 and was already active, just confirm and exit.
    if (action === '1' && wasActive) {
        process.stdout.write((0, MatrixTheme_1.paint)('keeping existing credentials\n', MatrixTheme_1.MATRIX.body) + '\n');
    }
}
// ─── Section: API Keys ───────────────────────────────────────────────
async function sectionApiKeys(root) {
    const env = readEnv(root);
    const slots = [
        { envName: 'OPENAI_API_KEY', slot: 'openai' },
        { envName: 'ANTHROPIC_API_KEY', slot: 'anthropic' },
        { envName: 'GEMINI_API_KEY', slot: 'gemini' },
        { envName: 'OPENROUTER_API_KEY', slot: 'openrouter' },
        { envName: 'ELEVENLABS_API_KEY', slot: 'elevenlabs' },
    ];
    process.stdout.write('\n' + (0, MatrixTheme_1.paint)('API Keys', MatrixTheme_1.MATRIX.head) + '\n');
    for (let i = 0; i < slots.length; i++) {
        const s = slots[i];
        process.stdout.write(`  ${(0, MatrixTheme_1.paint)(`${i + 1})`, MatrixTheme_1.MATRIX.body)} ${s.envName.padEnd(22)} ${(0, MatrixTheme_1.paint)(mask(env[s.envName]), MatrixTheme_1.MATRIX.tail)}\n`);
    }
    process.stdout.write(`  ${(0, MatrixTheme_1.paint)('6)', MatrixTheme_1.MATRIX.body)} return\n\n`);
    const pick = await ask('Choose [1-6]:');
    const idx = Number(pick) - 1;
    if (Number.isNaN(idx) || idx < 0 || idx >= slots.length)
        return;
    const s = slots[idx];
    process.stdout.write(`\nPaste new ${s.envName} (clear screen after):\n`);
    const value = await ask(`${s.envName}:`);
    if (!value) {
        process.stdout.write((0, MatrixTheme_1.paint)('(empty — kept current)\n', MatrixTheme_1.MATRIX.tail));
        return;
    }
    (0, InstallModules_1.saveApiKeysToEnv)(root, { [s.slot]: value });
    process.stdout.write((0, MatrixTheme_1.paint)(`✓ ${s.envName} saved\n`, MatrixTheme_1.MATRIX.head));
}
// ─── Section: Telegram ───────────────────────────────────────────────
async function sectionTelegram(root) {
    const env = readEnv(root);
    process.stdout.write('\n' + (0, MatrixTheme_1.paint)('Telegram Bot', MatrixTheme_1.MATRIX.head) + '\n');
    process.stdout.write(`  current TELEGRAM_BOT_TOKEN: ${(0, MatrixTheme_1.paint)(mask(env.TELEGRAM_BOT_TOKEN), MatrixTheme_1.MATRIX.tail)}\n`);
    process.stdout.write(`  current allowed user id:    ${(0, MatrixTheme_1.paint)(env.OPENLIFE_TELEGRAM_ALLOWED_USER_ID || '(unset)', MatrixTheme_1.MATRIX.tail)}\n\n`);
    const action = await (0, ProviderSelector_1.numberedChoice)('What to do?', [
        { key: '1', label: 'Set / replace token' },
        { key: '2', label: 'Set / replace allowed user id' },
        { key: '3', label: 'Validate current token (getMe)' },
        { key: '4', label: 'Return' },
    ]);
    if (!action || action === '4')
        return;
    if (action === '1') {
        const token = await ask('Paste TELEGRAM_BOT_TOKEN:');
        if (!token)
            return;
        const val = (0, InstallModules_1.validateTelegramToken)(token);
        if (!val.ok) {
            process.stdout.write((0, MatrixTheme_1.paint)(`✗ ${val.detail}\n`, MatrixTheme_1.MATRIX.err));
            return;
        }
        (0, InstallModules_1.saveTelegramConfig)(root, token);
        process.stdout.write((0, MatrixTheme_1.paint)(`✓ token saved (${val.detail})\n`, MatrixTheme_1.MATRIX.head));
    }
    else if (action === '2') {
        const uid = await ask('Allowed Telegram user id (digits):');
        if (!/^\d+$/.test(uid)) {
            process.stdout.write((0, MatrixTheme_1.paint)('not a numeric id\n', MatrixTheme_1.MATRIX.err));
            return;
        }
        const envPath = path.join(root, '.env');
        const cur = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf-8') : '';
        const lines = cur.split('\n').filter(Boolean).filter((l) => !l.startsWith('OPENLIFE_TELEGRAM_ALLOWED_USER_ID='));
        lines.push(`OPENLIFE_TELEGRAM_ALLOWED_USER_ID=${uid}`);
        fs.writeFileSync(envPath, lines.join('\n') + '\n', 'utf-8');
        process.stdout.write((0, MatrixTheme_1.paint)('✓ allowed user id saved\n', MatrixTheme_1.MATRIX.head));
    }
    else if (action === '3') {
        const val = (0, InstallModules_1.validateTelegramToken)(env.TELEGRAM_BOT_TOKEN);
        const color = val.ok ? MatrixTheme_1.MATRIX.head : MatrixTheme_1.MATRIX.err;
        process.stdout.write((0, MatrixTheme_1.paint)(`${val.ok ? '✓' : '✗'} ${val.detail}\n`, color));
    }
}
// ─── Section: Voice / TTS ────────────────────────────────────────────
async function sectionVoice(root) {
    const voicePath = path.join(root, '.openlife', 'voice.config.json');
    let cfg = { enabled: false, provider: 'gemini' };
    try {
        if (fs.existsSync(voicePath))
            cfg = { ...cfg, ...JSON.parse(fs.readFileSync(voicePath, 'utf-8')) };
    }
    catch { /* defaults */ }
    process.stdout.write('\n' + (0, MatrixTheme_1.paint)('Voice / TTS', MatrixTheme_1.MATRIX.head) + '\n');
    process.stdout.write(`  enabled:  ${cfg.enabled ? (0, MatrixTheme_1.paint)('yes', MatrixTheme_1.MATRIX.head) : (0, MatrixTheme_1.paint)('no', MatrixTheme_1.MATRIX.tail)}\n`);
    process.stdout.write(`  provider: ${(0, MatrixTheme_1.paint)(cfg.provider, MatrixTheme_1.MATRIX.body)}\n`);
    process.stdout.write(`  voice:    ${(0, MatrixTheme_1.paint)(cfg.voice || '(default)', MatrixTheme_1.MATRIX.body)}\n\n`);
    const action = await (0, ProviderSelector_1.numberedChoice)('What to change?', [
        { key: '1', label: cfg.enabled ? 'Disable TTS' : 'Enable TTS' },
        { key: '2', label: 'Change provider (gemini / elevenlabs / system)' },
        { key: '3', label: 'Return' },
    ]);
    if (!action || action === '3')
        return;
    if (action === '1')
        cfg.enabled = !cfg.enabled;
    if (action === '2') {
        const newProvider = await ask('Provider [gemini / elevenlabs / system]:');
        if (['gemini', 'elevenlabs', 'system'].includes(newProvider))
            cfg.provider = newProvider;
    }
    try {
        fs.mkdirSync(path.dirname(voicePath), { recursive: true });
    }
    catch { /* exists */ }
    fs.writeFileSync(voicePath, JSON.stringify(cfg, null, 2) + '\n', 'utf-8');
    process.stdout.write((0, MatrixTheme_1.paint)('✓ voice config saved\n', MatrixTheme_1.MATRIX.head));
}
// ─── Section: Daemon ─────────────────────────────────────────────────
async function sectionDaemon(root) {
    process.stdout.write('\n' + (0, MatrixTheme_1.paint)('Daemon', MatrixTheme_1.MATRIX.head) + '\n');
    const action = await (0, ProviderSelector_1.numberedChoice)('Action?', [
        { key: '1', label: 'status' },
        { key: '2', label: 'start --daemon (background)' },
        { key: '3', label: 'restart' },
        { key: '4', label: 'stop (restart command)' },
        { key: '5', label: 'return' },
    ]);
    if (!action || action === '5')
        return;
    const bin = path.join(root, 'bin', 'openlife.js');
    try {
        if (action === '1') {
            const out = (0, child_process_1.execFileSync)(process.execPath, [bin, 'status'], { encoding: 'utf-8' });
            process.stdout.write('\n' + out + '\n');
        }
        else if (action === '2') {
            // detach
            const { spawn } = require('child_process');
            const child = spawn(process.execPath, [bin, 'start', '--daemon'], { detached: true, stdio: 'ignore' });
            child.unref();
            process.stdout.write((0, MatrixTheme_1.paint)('✓ daemon spawn requested\n', MatrixTheme_1.MATRIX.head));
        }
        else if (action === '3' || action === '4') {
            const out = (0, child_process_1.execFileSync)(process.execPath, [bin, 'restart'], { encoding: 'utf-8' });
            process.stdout.write('\n' + out + '\n');
        }
    }
    catch (e) {
        process.stdout.write((0, MatrixTheme_1.paint)(`✗ ${e instanceof Error ? e.message : String(e)}\n`, MatrixTheme_1.MATRIX.err));
    }
}
// ─── Main entry ──────────────────────────────────────────────────────
async function runConfig(opts = {}) {
    const root = opts.cwd ?? process.cwd();
    if (opts.focus === 'provider') {
        await sectionProvider(root);
        return;
    }
    if (opts.focus === 'api-keys') {
        await sectionApiKeys(root);
        return;
    }
    if (opts.focus === 'telegram') {
        await sectionTelegram(root);
        return;
    }
    if (opts.focus === 'voice') {
        await sectionVoice(root);
        return;
    }
    if (opts.focus === 'daemon') {
        await sectionDaemon(root);
        return;
    }
    // eslint-disable-next-line no-constant-condition
    while (true) {
        const env = readEnv(root);
        const models = readModelsJson(root);
        const keySummary = `openai ${env.OPENAI_API_KEY ? '✓' : '·'}  anthropic ${env.ANTHROPIC_API_KEY ? '✓' : '·'}  gemini ${env.GEMINI_API_KEY ? '✓' : '·'}`;
        const pick = await (0, ProviderSelector_1.selectOne)({
            title: (0, MatrixTheme_1.paint)('OpenLife Configuration', MatrixTheme_1.MATRIX.head),
            options: [
                { value: '1', label: 'Provider & Model', hint: models.primary?.raw || 'unknown' },
                { value: '2', label: 'API Keys', hint: keySummary },
                { value: '3', label: 'Telegram Bot', hint: env.TELEGRAM_BOT_TOKEN ? '✓' : '·' },
                { value: '4', label: 'Voice / TTS', hint: '' },
                { value: '5', label: 'Daemon', hint: '' },
                { value: '6', label: 'Exit', hint: '' },
            ],
        });
        if (!pick || pick === '6')
            return;
        if (pick === '1')
            await sectionProvider(root);
        else if (pick === '2')
            await sectionApiKeys(root);
        else if (pick === '3')
            await sectionTelegram(root);
        else if (pick === '4')
            await sectionVoice(root);
        else if (pick === '5')
            await sectionDaemon(root);
    }
}
