"use strict";
/**
 * CoreRemoteCommand — Telegram `/core <verb> [args...]` router.
 *
 * v1.11.0 — Core ↔ Agent Bridge (Phase 4).
 *
 * Routing decision per verb:
 *   - status, memory, agents → answered DIRECTLY by the daemon (zero
 *     latency — they read shared state, no need to round-trip through the
 *     CLI inbox).
 *   - run, flow              → enqueued in ~/.openlife/cli-inbox/ for the
 *     core CLI to consume via `openlife bridge pull`. These verbs are
 *     cwd-sensitive and only the core CLI has the right working directory.
 *
 * The AUTHORIZATION layer (single-user allowlist via
 * OPENLIFE_TELEGRAM_ALLOWED_USER_ID) is enforced by Gateway BEFORE this
 * router runs. CoreRemoteCommand never decides who can call it.
 */
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.CoreRemoteCommand = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const CliInbox_1 = require("../cli/CliInbox");
const CorePresence_1 = require("../cli/CorePresence");
const DIRECT_VERBS = new Set(['status', 'memory', 'agents', 'story', 'stories', 'epics']);
const INBOX_VERBS = new Set(['run', 'flow']);
const SUPPORTED_VERBS = new Set([...DIRECT_VERBS, ...INBOX_VERBS]);
class CoreRemoteCommand {
    /** Parse the trailing portion of a `/core ` command into verb + args. */
    static parse(input) {
        const trimmed = input.trim();
        if (!trimmed.startsWith('/core'))
            return null;
        const after = trimmed.slice(5).trim();
        if (!after)
            return { verb: 'status', args: [] }; // bare `/core` → status
        const tokens = after.split(/\s+/);
        const verb = tokens[0].toLowerCase();
        const args = tokens.slice(1);
        return { verb, args };
    }
    /** Route a parsed `/core ...` command. Returns the Telegram reply text. */
    static async handle(input) {
        const parsed = CoreRemoteCommand.parse(input);
        if (!parsed) {
            return { reply: 'Comando /core inválido. Use: /core status | run <task> | memory <query> | agents <id> | flow run <id>', messageId: '', synchronous: false, routed: 'rejected' };
        }
        if (!SUPPORTED_VERBS.has(parsed.verb)) {
            return {
                reply: `Verbo /core não suportado: "${parsed.verb}". Suportados: ${Array.from(SUPPORTED_VERBS).join(', ')}`,
                messageId: '',
                synchronous: false,
                routed: 'rejected',
            };
        }
        if (DIRECT_VERBS.has(parsed.verb)) {
            const reply = await CoreRemoteCommand.handleDirect(parsed.verb, parsed.args);
            return { reply, messageId: '', synchronous: true, routed: 'direct' };
        }
        // Inbox-routed verbs (run, flow). Always fire-and-forget — the core CLI
        // processes them when the user invokes `openlife bridge pull`.
        const presence = CorePresence_1.CorePresence.read();
        const targetCwd = presence?.cwd;
        const messageBase = {
            ttlSeconds: 86_400,
            kind: 'command',
            origin: 'telegram',
            verb: parsed.verb,
            args: parsed.args,
            requireSync: false,
            expectedReplyKind: 'text',
            targetCwd,
        };
        const id = CliInbox_1.CliInbox.enqueue(messageBase);
        const argsRendered = parsed.args.length ? ` ${parsed.args.join(' ')}` : '';
        const cwdRendered = targetCwd ? targetCwd : 'unknown';
        return {
            reply: `\`${parsed.verb}${argsRendered}\` enfileirado para o core CLI (id=${id}, cwd=${cwdRendered}). Use \`openlife bridge pull\` no core para consumir.`,
            messageId: id,
            synchronous: false,
            routed: 'inbox',
        };
    }
    // ─── Direct handlers (answered by daemon, no inbox round-trip) ───────────
    static async handleDirect(verb, args) {
        switch (verb) {
            case 'status': return CoreRemoteCommand.directStatus();
            case 'memory': return CoreRemoteCommand.directMemory(args.join(' '));
            case 'agents': return CoreRemoteCommand.directAgents(args[0]);
            case 'story': return await CoreRemoteCommand.directStory(args[0]);
            case 'stories': return await CoreRemoteCommand.directStoryList(args);
            case 'epics': return await CoreRemoteCommand.directEpics();
            default: return `Verbo direto não implementado: ${verb}`;
        }
    }
    static directStatus() {
        const presence = CorePresence_1.CorePresence.read();
        const ageS = CorePresence_1.CorePresence.ageSeconds();
        const fresh = CorePresence_1.CorePresence.isFresh();
        if (!presence) {
            return 'Core CLI não foi invocado recentemente nesta máquina (sem core-presence.json).';
        }
        return [
            `Core CLI presence: ${fresh ? 'FRESH' : 'STALE'} (age ${ageS}s)`,
            `  pid: ${presence.pid}`,
            `  cwd: ${presence.cwd}`,
            `  cmd: ${presence.command}`,
            `  ts:  ${presence.invokedAt}`,
        ].join('\n');
    }
    static async directMemory(query) {
        if (!query || !query.trim())
            return 'Uso: /core memory <query>';
        try {
            // Lazy require — OmniMemory pulls heavy deps.
            const { OmniMemory } = require('../memory/OmniMemory');
            const omni = new OmniMemory();
            const result = await omni.search(query);
            if (!result || !String(result).trim()) {
                return `Sem hits para "${query}".`;
            }
            return String(result).slice(0, 3500); // Telegram message cap safety
        }
        catch (err) {
            return `Erro na busca de memória: ${err instanceof Error ? err.message : String(err)}`;
        }
    }
    static directAgents(id) {
        // Light-weight catalog read — we don't depend on full CatalogRegistry to
        // keep this path responsive. Falls back gracefully if catalog is absent.
        const candidates = [
            path.join(process.cwd(), '.catalog', 'agents'),
            path.join(process.cwd(), '.openlife', 'method', 'agents'),
        ];
        let baseDir = null;
        for (const dir of candidates) {
            if (fs.existsSync(dir)) {
                baseDir = dir;
                break;
            }
        }
        if (!baseDir)
            return 'Catálogo de agents não encontrado neste cwd.';
        if (!id) {
            try {
                const names = fs.readdirSync(baseDir)
                    .filter(name => !name.startsWith('.'))
                    .map(name => name.replace(/\.md$/i, ''));
                return names.length ? `Agents disponíveis:\n${names.map(n => `  • ${n}`).join('\n')}` : 'Catálogo de agents vazio.';
            }
            catch (err) {
                return `Falha ao ler catálogo: ${err instanceof Error ? err.message : String(err)}`;
            }
        }
        const candidates2 = [
            path.join(baseDir, `${id}.md`),
            path.join(baseDir, id, 'index.md'),
            path.join(baseDir, id, `${id}.md`),
        ];
        for (const f of candidates2) {
            if (fs.existsSync(f) && fs.statSync(f).isFile()) {
                try {
                    const content = fs.readFileSync(f, 'utf-8').slice(0, 3500);
                    return content;
                }
                catch { /* fall through */ }
            }
        }
        return `Agent "${id}" não encontrado no catálogo (${baseDir}).`;
    }
    // ─── v1.12.0 — story direct handlers ─────────────────────────────────────
    static async directStory(id) {
        if (!id)
            return 'Uso: /core story <id> (ex: /core story 3.3)';
        try {
            const { StoryRegistry } = require('./story/StoryRegistry');
            const reg = new StoryRegistry();
            await reg.load();
            const s = reg.get(id);
            if (!s)
                return `Story "${id}" não encontrada.`;
            const dep = s.dependsOn.length ? `\n  Depends: ${s.dependsOn.join(', ')}` : '';
            const sev = s.severity ? `\n  Severity: ${s.severity}` : '';
            const clu = s.cluster ? `\n  Cluster: ${s.cluster}` : '';
            const openACs = s.acceptanceCriteria.filter((a) => !a.checked).length;
            const acSummary = s.acceptanceCriteria.length
                ? `\n  ACs: ${openACs}/${s.acceptanceCriteria.length} open`
                : '';
            const desc = s.description ? `\n\n${String(s.description).slice(0, 1800)}` : '';
            return `Story ${s.id} — ${s.title}\n  Epic: ${s.epicId}\n  Status: ${s.status}${sev}${clu}${dep}${acSummary}${desc}`.slice(0, 3800);
        }
        catch (err) {
            return `Erro ao carregar story: ${err instanceof Error ? err.message : String(err)}`;
        }
    }
    static async directStoryList(args) {
        try {
            const { StoryRegistry } = require('./story/StoryRegistry');
            const reg = new StoryRegistry();
            await reg.load();
            const filter = {};
            for (const a of args) {
                if (a.startsWith('epic='))
                    filter.epicId = a.slice(5);
                else if (a.startsWith('status='))
                    filter.status = a.slice(7);
                else if (a.startsWith('cluster='))
                    filter.cluster = a.slice(8);
            }
            const stories = reg.list({ epicId: filter.epicId, status: filter.status, cluster: filter.cluster });
            if (!stories.length)
                return 'Nenhuma story encontrada com esses filtros.';
            const lines = stories
                .sort((a, b) => a.id.localeCompare(b.id, undefined, { numeric: true }))
                .slice(0, 30)
                .map((s) => `  ${s.id}  ${s.status.padEnd(11)}  ${s.title}`);
            const total = stories.length > 30 ? `\n(+${stories.length - 30} more…)` : '';
            return `Stories (${stories.length}):\n${lines.join('\n')}${total}`.slice(0, 3800);
        }
        catch (err) {
            return `Erro ao listar stories: ${err instanceof Error ? err.message : String(err)}`;
        }
    }
    static async directEpics() {
        try {
            const { StoryRegistry } = require('./story/StoryRegistry');
            const reg = new StoryRegistry();
            await reg.load();
            const epics = reg.listEpics();
            if (!epics.length)
                return 'Nenhum epic encontrado.';
            const lines = epics.map((e) => `  ${e.id} — ${e.status || '-'} — ${e.title} (${e.storyIds.length} stories)`);
            return `Epics (${epics.length}):\n${lines.join('\n')}`.slice(0, 3800);
        }
        catch (err) {
            return `Erro ao listar epics: ${err instanceof Error ? err.message : String(err)}`;
        }
    }
    /** Exposed for tests. */
    static _supportedVerbs() {
        return Array.from(SUPPORTED_VERBS);
    }
    static _directVerbs() {
        return Array.from(DIRECT_VERBS);
    }
}
exports.CoreRemoteCommand = CoreRemoteCommand;
