"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
const cli_prompt_detector_1 = require("../cli-prompt-detector");
function collect() {
    const events = [];
    const listener = (e) => events.push(e);
    cli_prompt_detector_1.cliPromptDetector.on('event', listener);
    return { events, unsubscribe: () => cli_prompt_detector_1.cliPromptDetector.off('event', listener) };
}
const FULL_MENU = [
    '  This session is 5d 5h old and 267.4k tokens.',
    '',
    '  Resuming the full session will consume...',
    '',
    '  ❯ 1. Resume from summary (recommended)',
    '    2. Resume full session as-is',
    "    3. Don't ask me again",
].join('\n');
(0, vitest_1.describe)('parseOptions', () => {
    (0, vitest_1.it)('提取三个 numbered options 含 label + recommended 标志', () => {
        const opts = (0, cli_prompt_detector_1.parseOptions)(FULL_MENU);
        (0, vitest_1.expect)(opts).toEqual([
            { digit: 1, label: 'Resume from summary', recommended: true },
            { digit: 2, label: 'Resume full session as-is', recommended: false },
            { digit: 3, label: "Don't ask me again", recommended: false },
        ]);
    });
    (0, vitest_1.it)('"(recommended)" 后缀也算 recommended', () => {
        const opts = (0, cli_prompt_detector_1.parseOptions)('  1. Foo (recommended)\n  2. Bar');
        (0, vitest_1.expect)(opts[0]?.recommended).toBe(true);
        (0, vitest_1.expect)(opts[0]?.label).toBe('Foo');
        (0, vitest_1.expect)(opts[1]?.recommended).toBe(false);
    });
    (0, vitest_1.it)('重复同 digit 取最新（Ink 重绘场景）', () => {
        const text = '  1. First version\n  2. B\n--- redraw ---\n  1. Updated\n  2. B';
        const opts = (0, cli_prompt_detector_1.parseOptions)(text);
        (0, vitest_1.expect)(opts.find((o) => o.digit === 1)?.label).toBe('Updated');
    });
    (0, vitest_1.it)('option 数字按 digit 排序而非出现顺序', () => {
        const text = '  3. Third\n  1. First\n  2. Second';
        (0, vitest_1.expect)((0, cli_prompt_detector_1.parseOptions)(text).map((o) => o.digit)).toEqual([1, 2, 3]);
    });
    (0, vitest_1.it)('无 digit 行不被识别', () => {
        const text = 'Just some prose. And "1." in the middle of a sentence is fine.';
        (0, vitest_1.expect)((0, cli_prompt_detector_1.parseOptions)(text)).toEqual([]);
    });
});
(0, vitest_1.describe)('cliPromptDetector', () => {
    (0, vitest_1.beforeEach)(() => {
        for (const pid of ['p1', 'p2', 'p_ansi', 'p_split', 'p_idem', 'p_reset', 'p_no_opts', 'p_update']) {
            cli_prompt_detector_1.cliPromptDetector.reset(pid);
        }
    });
    (0, vitest_1.it)('三短语 + ≥2 options → detected emit 含 options 数组', () => {
        const { events } = collect();
        cli_prompt_detector_1.cliPromptDetector.feed('p1', FULL_MENU);
        const detected = events.filter((e) => e.projectId === 'p1' && e.type === 'cli_prompt_detected');
        (0, vitest_1.expect)(detected).toHaveLength(1);
        if (detected[0]?.type === 'cli_prompt_detected') {
            (0, vitest_1.expect)(detected[0].options).toHaveLength(3);
            (0, vitest_1.expect)(detected[0].options[0]?.label).toBe('Resume from summary');
            (0, vitest_1.expect)(detected[0].options[0]?.recommended).toBe(true);
        }
    });
    (0, vitest_1.it)('三短语满足但 options 解析不到（CLI 改格式） → 不 emit detected', () => {
        const { events } = collect();
        // 三短语在文本里，但没有 "<digit>. label" 行（CLI 假设改为别的渲染）
        const broken = 'Resume from summary?\nResume full session?\nDon\'t ask me again? [Y/n]';
        cli_prompt_detector_1.cliPromptDetector.feed('p_no_opts', broken);
        (0, vitest_1.expect)(events.filter((e) => e.projectId === 'p_no_opts')).toHaveLength(0);
    });
    (0, vitest_1.it)('缺任一关键短语 → 不 detect', () => {
        const { events } = collect();
        cli_prompt_detector_1.cliPromptDetector.feed('p1', '  1. Resume from summary\n  2. Resume full session\n');
        (0, vitest_1.expect)(events.filter((e) => e.projectId === 'p1')).toHaveLength(0);
    });
    (0, vitest_1.it)('ANSI 转义码被 strip 后仍能匹配 + 解析 options', () => {
        const { events } = collect();
        const ansi = `\x1b[2K\x1b[36m❯ 1.\x1b[0m Resume from summary (recommended)\n    2. Resume full session as-is\n    3. Don't ask me again`;
        cli_prompt_detector_1.cliPromptDetector.feed('p_ansi', ansi);
        const detected = events.filter((e) => e.projectId === 'p_ansi' && e.type === 'cli_prompt_detected');
        (0, vitest_1.expect)(detected).toHaveLength(1);
        if (detected[0]?.type === 'cli_prompt_detected') {
            (0, vitest_1.expect)(detected[0].options).toHaveLength(3);
        }
    });
    (0, vitest_1.it)('v-20-b 根因：Ink TUI 用 CHA `\\x1b[<n>G` 替代字面空格，正确还原', () => {
        const { events } = collect();
        // Real Ink TUI output captured from Claude CLI 2.1.144 PTY (see investigation notes).
        // 每个单词间用 CHA 序列定位列号，没有任何字面空格字符 —— 旧 stripAnsi 会得到
        // "Resumefromsummary" 永远匹配不到 fingerprint。
        const inkReal = '\x1b[3G\x1b[38;2;87;105;247m❯\x1b[5G\x1b[38;2;102;102;102m1.\x1b[8G' +
            '\x1b[38;2;87;105;247mResume\x1b[15Gfrom\x1b[20Gsummary\x1b[28G(recommended)\x1b[39m\r\r\n' +
            '\x1b[5G\x1b[38;2;102;102;102m2.\x1b[8G\x1b[39mResume\x1b[15Gfull\x1b[20Gsession\x1b[28Gas-is\r\r\n' +
            "\x1b[5G\x1b[38;2;102;102;102m3.\x1b[8G\x1b[39mDon't\x1b[14Gask\x1b[18Gme\x1b[21Gagain\r\r\n";
        cli_prompt_detector_1.cliPromptDetector.feed('p_ink', inkReal);
        const detected = events.filter((e) => e.projectId === 'p_ink' && e.type === 'cli_prompt_detected');
        (0, vitest_1.expect)(detected).toHaveLength(1);
        if (detected[0]?.type === 'cli_prompt_detected') {
            (0, vitest_1.expect)(detected[0].options).toEqual([
                { digit: 1, label: 'Resume from summary', recommended: true },
                { digit: 2, label: 'Resume full session as-is', recommended: false },
                { digit: 3, label: "Don't ask me again", recommended: false },
            ]);
        }
        cli_prompt_detector_1.cliPromptDetector.reset('p_ink');
    });
    (0, vitest_1.it)('CUF `\\x1b[<n>C` 替换为 n 个空格', () => {
        const { events } = collect();
        // Hypothetical CLI using CUF instead of CHA to advance the cursor.
        // The detector should still see well-spaced text after strip.
        const cuf = "Resume\x1b[1Cfrom\x1b[1Csummary\nResume\x1b[1Cfull\x1b[1Csession\n  1. Resume from summary\n  2. Resume full session\n  3. Don't ask me again";
        cli_prompt_detector_1.cliPromptDetector.feed('p_cuf', cuf);
        const detected = events.filter((e) => e.projectId === 'p_cuf' && e.type === 'cli_prompt_detected');
        (0, vitest_1.expect)(detected.length).toBeGreaterThanOrEqual(1);
        cli_prompt_detector_1.cliPromptDetector.reset('p_cuf');
    });
    (0, vitest_1.it)('已 detected 状态下 options 未变 → 不重复 emit（debounced）', () => {
        const { events } = collect();
        cli_prompt_detector_1.cliPromptDetector.feed('p_idem', FULL_MENU);
        cli_prompt_detector_1.cliPromptDetector.feed('p_idem', FULL_MENU);
        cli_prompt_detector_1.cliPromptDetector.feed('p_idem', FULL_MENU);
        const detected = events.filter((e) => e.projectId === 'p_idem' && e.type === 'cli_prompt_detected');
        (0, vitest_1.expect)(detected).toHaveLength(1);
    });
    (0, vitest_1.it)('已 detected 状态下 options 变了（Ink ↑↓ 移 highlight）→ 重发 detected 更新 options', () => {
        const { events } = collect();
        cli_prompt_detector_1.cliPromptDetector.feed('p_update', FULL_MENU);
        // 用户按 ↓ 移 highlight 到 #2，Ink 重绘整段
        const after = [
            '  This session is 5d 5h old and 267.4k tokens.',
            '  Resuming the full session will consume...',
            '    1. Resume from summary',
            '  ❯ 2. Resume full session as-is',
            "    3. Don't ask me again",
        ].join('\n');
        cli_prompt_detector_1.cliPromptDetector.feed('p_update', after);
        const detected = events.filter((e) => e.projectId === 'p_update' && e.type === 'cli_prompt_detected');
        (0, vitest_1.expect)(detected.length).toBeGreaterThanOrEqual(2);
        const last = detected[detected.length - 1];
        if (last?.type === 'cli_prompt_detected') {
            (0, vitest_1.expect)(last.options[1]?.recommended).toBe(true);
            (0, vitest_1.expect)(last.options[0]?.recommended).toBe(false);
        }
    });
    (0, vitest_1.it)('detected 后 buffer 滚出关键词 → emit dismissed', () => {
        const { events } = collect();
        cli_prompt_detector_1.cliPromptDetector.feed('p1', FULL_MENU);
        const noise = 'x'.repeat(10 * 1024);
        cli_prompt_detector_1.cliPromptDetector.feed('p1', noise);
        const ev = events.filter((e) => e.projectId === 'p1');
        (0, vitest_1.expect)(ev.map((e) => e.type)).toEqual(['cli_prompt_detected', 'cli_prompt_dismissed']);
    });
    (0, vitest_1.it)('多 project 状态隔离', () => {
        const { events } = collect();
        cli_prompt_detector_1.cliPromptDetector.feed('p1', FULL_MENU);
        cli_prompt_detector_1.cliPromptDetector.feed('p2', 'unrelated output');
        (0, vitest_1.expect)(events.filter((e) => e.projectId === 'p1' && e.type === 'cli_prompt_detected')).toHaveLength(1);
        (0, vitest_1.expect)(events.filter((e) => e.projectId === 'p2')).toHaveLength(0);
    });
    (0, vitest_1.it)('reset 在 active 状态下触发 dismissed', () => {
        const { events } = collect();
        cli_prompt_detector_1.cliPromptDetector.feed('p_reset', FULL_MENU);
        cli_prompt_detector_1.cliPromptDetector.reset('p_reset');
        const ev = events.filter((e) => e.projectId === 'p_reset');
        (0, vitest_1.expect)(ev.map((e) => e.type)).toEqual(['cli_prompt_detected', 'cli_prompt_dismissed']);
    });
    (0, vitest_1.it)('reset 在无 active 状态下不发 dismissed', () => {
        const { events } = collect();
        cli_prompt_detector_1.cliPromptDetector.reset('p1');
        (0, vitest_1.expect)(events.filter((e) => e.projectId === 'p1')).toHaveLength(0);
    });
    (0, vitest_1.it)('getActive 返回当前 options 快照（REST 同步用）', () => {
        cli_prompt_detector_1.cliPromptDetector.feed('p1', FULL_MENU);
        const active = cli_prompt_detector_1.cliPromptDetector.getActive('p1');
        (0, vitest_1.expect)(active?.options).toHaveLength(3);
        (0, vitest_1.expect)(active?.options[0]?.label).toBe('Resume from summary');
    });
});
//# sourceMappingURL=cli-prompt-detector.test.js.map