"use strict";
// src/cli/ChatTui.ts
// OpenLife interactive chat REPL — Matrix 2026 theme.
//
// Entry point: `runChat()`. Bound to bare `openlife` invocation in src/index.ts
// when stdout is a TTY. Renders the OPENLIFE banner + Cristo silhouette, a
// catalog inventory panel, and a Matrix code-rain "thinking" region that
// streams Brain reasoning lines while the agent works.
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.buildInventoryStats = buildInventoryStats;
exports.runChatSmoke = runChatSmoke;
exports.runChat = runChat;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const readline = __importStar(require("readline"));
const ChatBanner_1 = require("./ChatBanner");
const MatrixRain_1 = require("./MatrixRain");
const MatrixTheme_1 = require("./MatrixTheme");
// eslint-disable-next-line no-control-regex
function stripAnsi(s) {
    return s.replace(/\x1b\[[0-9;]*[A-Za-z]/g, '').replace(/\x1b[^[]/g, '');
}
function topCatalogNames(dir, n) {
    try {
        return fs.readdirSync(dir)
            .filter(e => !e.startsWith('test-'))
            .filter(e => { try {
            return fs.statSync(path.join(dir, e)).isDirectory();
        }
        catch {
            return false;
        } })
            .slice(0, n);
    }
    catch {
        return [];
    }
}
function buildCatalogContext(stats) {
    const agents = topCatalogNames(path.join(stats.cwd, '.catalog', 'agents'), 12);
    const skills = topCatalogNames(path.join(stats.cwd, '.catalog', 'skills'), 12);
    const lines = [
        `Você é o OpenLife CLI ${stats.version}.`,
        `Catálogo disponível: ${stats.agents} agents, ${stats.squads} squads, ${stats.skills} skills, ${stats.mcps} MCPs.`,
    ];
    if (agents.length)
        lines.push(`Agents (amostra): ${agents.join(', ')}.`);
    if (skills.length)
        lines.push(`Skills (amostra): ${skills.join(', ')}.`);
    lines.push('Ao receber pedidos, referencie agents e skills pelo nome exato do catálogo.');
    return lines.join('\n');
}
function counts(dir) {
    try {
        if (!fs.existsSync(dir))
            return 0;
        return fs.readdirSync(dir).filter((entry) => {
            const p = path.join(dir, entry);
            try {
                return fs.statSync(p).isDirectory() && !entry.startsWith('test-');
            }
            catch {
                return false;
            }
        }).length;
    }
    catch {
        return 0;
    }
}
function readActiveModel(root) {
    try {
        const p = path.join(root, 'models.json');
        if (!fs.existsSync(p))
            return 'unknown';
        const j = JSON.parse(fs.readFileSync(p, 'utf-8'));
        return j.primary?.raw || 'unknown';
    }
    catch {
        return 'unknown';
    }
}
function genSessionId() {
    const d = new Date();
    const pad = (n, w = 2) => String(n).padStart(w, '0');
    const stamp = `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}_${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
    const rand = Math.random().toString(16).slice(2, 8);
    return `${stamp}_${rand}`;
}
function buildInventoryStats(root, sessionId) {
    const pkg = (() => {
        try {
            return JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf-8'));
        }
        catch {
            return { version: 'dev' };
        }
    })();
    return {
        version: `v${pkg.version}`,
        model: readActiveModel(root),
        sessionId,
        cwd: root,
        agents: counts(path.join(root, '.catalog', 'agents')),
        squads: counts(path.join(root, '.catalog', 'squads')),
        skills: counts(path.join(root, '.catalog', 'skills')),
        mcps: counts(path.join(root, '.catalog', 'mcps')),
        agentNames: topCatalogNames(path.join(root, '.catalog', 'agents'), 8),
        squadNames: topCatalogNames(path.join(root, '.catalog', 'squads'), 8),
        skillNames: topCatalogNames(path.join(root, '.catalog', 'skills'), 8),
        mcpNames: topCatalogNames(path.join(root, '.catalog', 'mcps'), 8),
    };
}
function printCatalogList(root, section) {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const reader = require('./CatalogReader');
    const fn = {
        agents: reader.listAgentIds,
        skills: reader.listSkillIds,
        squads: reader.listSquadIds,
        mcps: reader.listMcpIds,
    }[section];
    const ids = fn(root);
    if (ids.length === 0) {
        process.stdout.write((0, MatrixTheme_1.paint)(`  (catálogo vazio ou não encontrado em ${root})\n`, MatrixTheme_1.MATRIX.warn));
        return;
    }
    process.stdout.write((0, MatrixTheme_1.paint)(`${section} (${ids.length}):\n`, MatrixTheme_1.MATRIX.head));
    for (const id of ids) {
        process.stdout.write((0, MatrixTheme_1.paint)(`  ${id}\n`, MatrixTheme_1.MATRIX.body));
    }
    process.stdout.write((0, MatrixTheme_1.paint)(`  use /${section}-{nome} para detalhes\n`, MatrixTheme_1.MATRIX.tail));
}
function printCatalogDetail(root, section, id) {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const { getEntryDetail } = require('./CatalogReader');
    const detail = getEntryDetail(root, section, id);
    if (!detail) {
        process.stdout.write((0, MatrixTheme_1.paint)(`  "${id}" não encontrado em .catalog/${section}/\n`, MatrixTheme_1.MATRIX.warn));
        return;
    }
    process.stdout.write((0, MatrixTheme_1.paint)(`id:    `, MatrixTheme_1.MATRIX.head) + (0, MatrixTheme_1.paint)(`${detail.id}\n`, MatrixTheme_1.MATRIX.body));
    process.stdout.write((0, MatrixTheme_1.paint)(`name:  `, MatrixTheme_1.MATRIX.head) + (0, MatrixTheme_1.paint)(`${detail.name}\n`, MatrixTheme_1.MATRIX.body));
    if (detail.role)
        process.stdout.write((0, MatrixTheme_1.paint)(`role:  `, MatrixTheme_1.MATRIX.head) + (0, MatrixTheme_1.paint)(`${detail.role}\n`, MatrixTheme_1.MATRIX.body));
    if (detail.description)
        process.stdout.write((0, MatrixTheme_1.paint)(`desc:  `, MatrixTheme_1.MATRIX.head) + (0, MatrixTheme_1.paint)(`${detail.description}\n`, MatrixTheme_1.MATRIX.tail));
    if (detail.tags.length)
        process.stdout.write((0, MatrixTheme_1.paint)(`tags:  `, MatrixTheme_1.MATRIX.head) + (0, MatrixTheme_1.paint)(`${detail.tags.join(', ')}\n`, MatrixTheme_1.MATRIX.tail));
}
/**
 * Non-interactive chat availability check used by `openlife chat --test` and
 * by the installer. It deliberately avoids constructing Gateway/Brain so setup
 * can validate the terminal shell without hanging on provider credentials.
 */
function runChatSmoke(opts = {}) {
    const root = opts.cwd ?? process.cwd();
    try {
        const sessionId = 'smoke-test';
        const stats = buildInventoryStats(root, sessionId);
        const hasContext = !!stats.cwd && !!stats.sessionId && typeof stats.model === 'string';
        return {
            ok: hasContext,
            marker: hasContext ? 'OPENLIFE_CHAT_SMOKE_OK' : 'OPENLIFE_CHAT_SMOKE_FAIL',
            detail: hasContext ? 'chat shell context available' : 'chat shell context incomplete',
            sessionId: stats.sessionId,
            model: stats.model,
            cwd: stats.cwd,
        };
    }
    catch (err) {
        return {
            ok: false,
            marker: 'OPENLIFE_CHAT_SMOKE_FAIL',
            detail: err instanceof Error ? err.message : String(err),
            sessionId: 'smoke-test',
            model: 'unknown',
            cwd: root,
        };
    }
}
/** Built-in slash commands shown by `/help`. */
const SLASH_COMMANDS = [
    { name: '/help', desc: 'Show this command list' },
    { name: '/clear', desc: 'Clear screen and re-render banner' },
    { name: '/new', desc: 'Start a fresh session id (keeps the chat scroll)' },
    { name: '/history', desc: 'Show last 10 turns of the current session' },
    { name: '/save', desc: 'Persist the session log to disk now' },
    { name: '/model', desc: 'Print the currently active model' },
    { name: '/provider', desc: 'Open the provider selector (same as /config → Provider)' },
    { name: '/config', desc: 'Open the full configuration TUI' },
    { name: '/status', desc: 'Print daemon + executor status' },
    { name: '/doctor', desc: 'Run the doctor health checks' },
    { name: '/exit', desc: 'Leave the chat' },
    { name: '/agents', desc: 'Lista todos os agents do catálogo' },
    { name: '/agents-{id}', desc: 'Detalhe de um agent pelo id' },
    { name: '/skills', desc: 'Lista todas as skills do catálogo' },
    { name: '/skills-{id}', desc: 'Detalhe de uma skill pelo id' },
    { name: '/squads', desc: 'Lista todos os squads do catálogo' },
    { name: '/squads-{id}', desc: 'Detalhe de um squad pelo id' },
    { name: '/mcps', desc: 'Lista os MCPs configurados' },
];
function printHelp() {
    const head = (0, MatrixTheme_1.supportsColor)() ? MatrixTheme_1.MATRIX.head : '';
    const body = (0, MatrixTheme_1.supportsColor)() ? MatrixTheme_1.MATRIX.body : '';
    const tail = (0, MatrixTheme_1.supportsColor)() ? MatrixTheme_1.MATRIX.tail : '';
    const reset = (0, MatrixTheme_1.supportsColor)() ? MatrixTheme_1.ANSI.reset : '';
    process.stdout.write(`\n${head}Commands${reset}\n`);
    for (const c of SLASH_COMMANDS) {
        process.stdout.write(`  ${body}${c.name.padEnd(12)}${reset} ${tail}${c.desc}${reset}\n`);
    }
    process.stdout.write('\n');
}
function appendSessionLog(root, sessionId, entry) {
    try {
        const dir = path.join(root, '.openlife', 'chat-sessions');
        if (!fs.existsSync(dir))
            fs.mkdirSync(dir, { recursive: true });
        fs.appendFileSync(path.join(dir, `${sessionId}.jsonl`), JSON.stringify(entry) + '\n', 'utf-8');
    }
    catch { /* logging is best-effort; never break the chat */ }
}
async function runChat(opts = {}) {
    const root = opts.cwd ?? process.cwd();
    let sessionId = genSessionId();
    let stats = buildInventoryStats(root, sessionId);
    // Boot output — banner + inventory panel + welcome
    process.stdout.write((0, ChatBanner_1.bootOutput)(stats));
    // Warn early if primary model's API key is missing (avoids silent failure on first message)
    const primaryProvider = stats.model.split('/')[0];
    const KEY_MAP = {
        'gemini-api': 'GEMINI_API_KEY',
        'openai-api': 'OPENAI_API_KEY',
        'anthropic': 'ANTHROPIC_API_KEY',
    };
    const requiredKey = KEY_MAP[primaryProvider];
    if (requiredKey && !process.env[requiredKey]) {
        process.stdout.write((0, MatrixTheme_1.paint)(`⚠  ${requiredKey} não encontrada — rode: openlife install\n`, MatrixTheme_1.MATRIX.warn));
    }
    // Build catalog context once — injected as system context in every processChat call
    const catalogContext = buildCatalogContext(stats);
    appendSessionLog(root, sessionId, { ts: new Date().toISOString(), role: 'system', text: 'session_start' });
    // Lazy require Gateway to keep --help fast (banner test runs without it).
    const lazyGateway = () => {
        try {
            const { Gateway } = require('../orchestrator/Gateway');
            const gw = new Gateway();
            return gw;
        }
        catch (err) {
            const msg = err instanceof Error ? err.message : String(err);
            process.stdout.write((0, MatrixTheme_1.paint)(`\n[chat] Gateway unavailable: ${msg}\n`, MatrixTheme_1.MATRIX.warn) + '\n');
            return null;
        }
    };
    // Build a readline interface for the input prompt.
    const promptSymbol = (0, MatrixTheme_1.supportsColor)() ? `${MatrixTheme_1.MATRIX.head}›${MatrixTheme_1.ANSI.reset} ` : '› ';
    const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
    const askLine = () => new Promise((resolve) => {
        rl.question(promptSymbol, (answer) => resolve(answer));
    });
    const cleanShutdown = () => {
        rl.close();
        process.stdout.write(MatrixTheme_1.ANSI.showCursor + MatrixTheme_1.ANSI.reset);
    };
    process.on('SIGINT', () => { cleanShutdown(); process.exit(130); });
    // Main REPL loop
    // eslint-disable-next-line no-constant-condition
    while (true) {
        const raw = (await askLine()).trim();
        if (!raw)
            continue;
        if (raw === '/exit' || raw === '/quit') {
            process.stdout.write((0, MatrixTheme_1.paint)(`\nResume with: openlife --session ${sessionId}\n`, MatrixTheme_1.MATRIX.tail) + '\n');
            cleanShutdown();
            return;
        }
        if (raw === '/help') {
            printHelp();
            continue;
        }
        if (raw === '/clear') {
            process.stdout.write(MatrixTheme_1.ANSI.clearScreen);
            process.stdout.write((0, ChatBanner_1.bootOutput)(stats));
            continue;
        }
        if (raw === '/new') {
            sessionId = genSessionId();
            stats = buildInventoryStats(root, sessionId);
            process.stdout.write((0, MatrixTheme_1.paint)(`\nNew session: ${sessionId}\n`, MatrixTheme_1.MATRIX.head) + '\n');
            appendSessionLog(root, sessionId, { ts: new Date().toISOString(), role: 'system', text: 'session_start' });
            continue;
        }
        if (raw === '/model') {
            process.stdout.write((0, MatrixTheme_1.paint)(`\nactive model: ${readActiveModel(root)}\n`, MatrixTheme_1.MATRIX.body) + '\n');
            continue;
        }
        if (raw === '/status') {
            try {
                const { execFileSync } = require('child_process');
                const out = execFileSync(process.execPath, [path.join(root, 'bin', 'openlife.js'), 'status'], { encoding: 'utf-8' });
                process.stdout.write('\n' + out + '\n');
            }
            catch (e) {
                process.stdout.write((0, MatrixTheme_1.paint)(`\nstatus failed: ${e instanceof Error ? e.message : String(e)}\n`, MatrixTheme_1.MATRIX.err) + '\n');
            }
            continue;
        }
        if (raw === '/doctor') {
            try {
                const { execFileSync } = require('child_process');
                const out = execFileSync(process.execPath, [path.join(root, 'bin', 'openlife.js'), 'doctor'], { encoding: 'utf-8' });
                process.stdout.write('\n' + out + '\n');
            }
            catch (e) {
                process.stdout.write((0, MatrixTheme_1.paint)(`\ndoctor failed: ${e instanceof Error ? e.message : String(e)}\n`, MatrixTheme_1.MATRIX.err) + '\n');
            }
            continue;
        }
        if (raw === '/config' || raw === '/provider') {
            try {
                const { runConfig } = require('./ConfigTui');
                const onlyProvider = raw === '/provider';
                await runConfig({ cwd: root, focus: onlyProvider ? 'provider' : undefined });
                // Re-render banner after returning so the chat scroll context is preserved.
                stats = buildInventoryStats(root, sessionId);
                process.stdout.write((0, ChatBanner_1.bootOutput)(stats));
            }
            catch (e) {
                process.stdout.write((0, MatrixTheme_1.paint)(`\nconfig failed: ${e instanceof Error ? e.message : String(e)}\n`, MatrixTheme_1.MATRIX.err) + '\n');
            }
            continue;
        }
        if (raw === '/history' || raw === '/save') {
            const file = path.join(root, '.openlife', 'chat-sessions', `${sessionId}.jsonl`);
            if (raw === '/save') {
                process.stdout.write((0, MatrixTheme_1.paint)(`\nsession persisted: ${file}\n`, MatrixTheme_1.MATRIX.head) + '\n');
            }
            else {
                try {
                    const data = fs.readFileSync(file, 'utf-8').trim().split('\n').slice(-20).map(l => JSON.parse(l));
                    process.stdout.write('\n');
                    for (const e of data) {
                        const tag = e.role === 'user' ? '●' : e.role === 'assistant' ? '◢' : '·';
                        process.stdout.write(`  ${(0, MatrixTheme_1.paint)(tag, MatrixTheme_1.MATRIX.head)} ${e.role}: ${e.text.split('\n')[0].slice(0, 100)}\n`);
                    }
                    process.stdout.write('\n');
                }
                catch (err) {
                    process.stdout.write((0, MatrixTheme_1.paint)(`\nno history yet\n`, MatrixTheme_1.MATRIX.tail) + '\n');
                }
            }
            continue;
        }
        if (raw === '/agents') {
            printCatalogList(root, 'agents');
            continue;
        }
        if (raw === '/skills') {
            printCatalogList(root, 'skills');
            continue;
        }
        if (raw === '/squads') {
            printCatalogList(root, 'squads');
            continue;
        }
        if (raw === '/mcps') {
            printCatalogList(root, 'mcps');
            continue;
        }
        if (raw.startsWith('/agents-')) {
            printCatalogDetail(root, 'agents', raw.slice(8));
            continue;
        }
        if (raw.startsWith('/skills-')) {
            printCatalogDetail(root, 'skills', raw.slice(8));
            continue;
        }
        if (raw.startsWith('/squads-')) {
            printCatalogDetail(root, 'squads', raw.slice(8));
            continue;
        }
        if (raw.startsWith('/mcps-')) {
            process.stdout.write((0, MatrixTheme_1.paint)('  MCP detail not available — use /mcps to list.\n', MatrixTheme_1.MATRIX.tail));
            continue;
        }
        // Anything else → user message. Log, render header, fire backend, render response.
        const userTag = (0, MatrixTheme_1.paint)('●', MatrixTheme_1.MATRIX.head);
        process.stdout.write(`${userTag} ${raw}\n`);
        appendSessionLog(root, sessionId, { ts: new Date().toISOString(), role: 'user', text: raw });
        const gw = lazyGateway();
        if (!gw) {
            process.stdout.write((0, MatrixTheme_1.paint)('  Gateway unavailable — start the daemon first.\n', MatrixTheme_1.MATRIX.warn) + '\n');
            continue;
        }
        // Reserve a region for Matrix rain. Width = min(60, columns - 4).
        const cols = process.stdout.columns ?? 80;
        const rows = process.stdout.rows ?? 24;
        const rainWidth = Math.min(30, Math.max(20, Math.floor(cols / 3)));
        const panelWidth = Math.max(20, cols - rainWidth - 4);
        const rainRow = Math.max(2, rows - 10);
        const rainHeight = 8;
        const reasoningLines = [];
        const sidePanelLines = () => {
            const tail = reasoningLines.slice(-rainHeight);
            while (tail.length < rainHeight)
                tail.unshift('');
            return tail.map(line => (line.length > panelWidth ? line.slice(0, panelWidth - 1) + '…' : line));
        };
        // Allocate vertical space so rain has somewhere to render.
        for (let i = 0; i < rainHeight; i++)
            process.stdout.write('\n');
        const rain = (0, MatrixRain_1.startRain)({
            row: rainRow,
            col: 3,
            width: rainWidth + 1 + panelWidth,
            height: rainHeight,
            rainWidth,
            sidePanelLines,
        });
        const startedAt = Date.now();
        let answer = '';
        try {
            answer = await gw.processChat('cli-user', raw, {
                source: 'cli',
                systemContext: catalogContext,
                onReasoning: (chunk) => {
                    reasoningLines.push(stripAnsi(chunk));
                    if (reasoningLines.length > 200)
                        reasoningLines.splice(0, reasoningLines.length - 200);
                },
            });
        }
        catch (err) {
            answer = `[chat error] ${err instanceof Error ? err.message : String(err)}`;
        }
        rain.stop();
        const elapsed = ((Date.now() - startedAt) / 1000).toFixed(1);
        const aiTag = (0, MatrixTheme_1.paint)('◢', MatrixTheme_1.MATRIX.head);
        process.stdout.write(`\n${aiTag} ${answer}\n`);
        process.stdout.write((0, MatrixTheme_1.paint)(`  (${elapsed}s · ${readActiveModel(root)})\n`, MatrixTheme_1.MATRIX.tail) + '\n');
        appendSessionLog(root, sessionId, { ts: new Date().toISOString(), role: 'assistant', text: answer });
    }
}
