import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import React from "react";
import { Box, Text, renderToString } from "ink";
import { describe, expect, it } from "vitest";
import stripAnsi from "strip-ansi";
import stringWidth from "string-width";
import { UPDATE_NOTICE_TEXT } from "./app-items.js";
import { serializeCompletedItemToTerminalHistory } from "./terminal-history.js";
import { loadTheme, ThemeContext } from "./theme/theme.js";
import { TerminalSizeProvider } from "./hooks/useTerminalSize.js";
import { ToolExecution } from "./components/ToolExecution.js";
import { ToolGroupExecution } from "./components/ToolGroupExecution.js";
import { ServerToolExecution } from "./components/ServerToolExecution.js";
import { SubAgentPanel } from "./components/SubAgentPanel.js";
import { CompactionDone, CompactionSpinner } from "./components/CompactionNotice.js";
import { LANGUAGE_DISPLAY_NAMES } from "../core/language-detector.js";
import { AssistantMessage } from "./components/AssistantMessage.js";
import { IdealHookMessage } from "./components/IdealHookMessage.js";
import { UserMessage } from "./components/UserMessage.js";
import { Banner } from "./components/Banner.js";
import { SessionSummaryDisplay } from "./components/SessionSummary.js";
import { PlanModeLogo } from "./components/PlanModeLogo.js";
const TERMINAL_COLUMNS = 68;
const theme = loadTheme("dark");
const context = {
    theme,
    columns: TERMINAL_COLUMNS,
    version: "test",
    model: "test-model",
    provider: "anthropic",
    cwd: "/tmp/test",
};
function cleanLines(value) {
    return stripAnsi(value)
        .replace(/\r/g, "\n")
        .split("\n")
        .filter((line) => line.length > 0);
}
function renderLive(element) {
    const originalColumns = process.stdout.columns;
    const originalRows = process.stdout.rows;
    process.stdout.columns = TERMINAL_COLUMNS;
    process.stdout.rows = 20;
    try {
        return cleanLines(renderToString(_jsx(ThemeContext.Provider, { value: theme, children: _jsx(TerminalSizeProvider, { children: element }) }), { columns: TERMINAL_COLUMNS }));
    }
    finally {
        process.stdout.columns = originalColumns;
        process.stdout.rows = originalRows;
    }
}
function renderHistory(item) {
    return cleanLines(serializeCompletedItemToTerminalHistory(item, context));
}
function normalizeSessionSummaryLine(line) {
    if (/^[\s╭╰─╮╯]+$/u.test(line))
        return null;
    const boxed = line.match(/│(.*)│/u)?.[1] ?? line;
    const trimmed = boxed.trim();
    if (!trimmed)
        return null;
    return trimmed.replace(/\s{2,}/gu, ": ").replace(/^([^:]+): (.+)$/u, "$1: $2");
}
function normalizeParityLines(kind, lines) {
    // Banner art rows carry trailing padding spaces in the history serializer,
    // but Ink trims trailing whitespace per line in the live render. Trailing
    // whitespace is invisible, so compare both with trailing space stripped.
    if (kind === "user" || kind === "banner")
        return lines.map((line) => line.trimEnd());
    if (kind === "session_summary") {
        return lines.map(normalizeSessionSummaryLine).filter((line) => line !== null);
    }
    if (kind === "style_pack" || kind === "setup_hint" || kind === "update_notice") {
        return lines.map((line) => line.replace(/─/g, "").replace(/ +(?=│$)/u, ""));
    }
    return [...lines];
}
function renderQueuedLiveItem(item, itemTheme) {
    const imageSuffix = item.imageCount
        ? ` (+${item.imageCount} image${item.imageCount > 1 ? "s" : ""})`
        : "";
    const videoSuffix = item.videoCount
        ? ` (+${item.videoCount} video${item.videoCount > 1 ? "s" : ""})`
        : "";
    const suffix = `${imageSuffix}${videoSuffix}`;
    return (_jsxs(Box, { flexDirection: "row", paddingLeft: 1, marginTop: 1, flexShrink: 1, children: [_jsx(Box, { width: 2, flexShrink: 0, children: _jsx(Text, { color: itemTheme.warning, bold: true, children: "• " }) }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsxs(Text, { color: itemTheme.text, wrap: "wrap", children: [_jsx(Text, { color: itemTheme.textDim, children: "Queued: " }), item.text || "(empty)", suffix] }) })] }));
}
function renderStatusLive(glyph, content, glyphColor, itemTheme, options = {}) {
    return (_jsxs(Box, { flexDirection: "row", paddingLeft: 1, marginTop: 1, flexShrink: 1, children: [_jsx(Box, { width: 2, flexShrink: 0, children: _jsx(Text, { color: glyphColor, bold: options.bold ?? true, children: glyph }) }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsx(Text, { color: options.muted ? itemTheme.textDim : itemTheme.commandColor, bold: options.bold, wrap: "wrap", children: content }) })] }));
}
function renderStylePackLive(item, itemTheme) {
    const names = item.added.map((id) => LANGUAGE_DISPLAY_NAMES[id]);
    const headerLabel = item.added.length > 1 ? "STYLE PACKS ACTIVE" : "STYLE PACK ACTIVE";
    return (_jsx(Box, { paddingLeft: 1, marginTop: 1, flexShrink: 1, children: _jsxs(Box, { flexShrink: 1, flexDirection: "column", borderStyle: "round", borderColor: itemTheme.language, paddingX: 1, children: [_jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: itemTheme.language, bold: true, children: "◆ " }), _jsx(Text, { color: itemTheme.language, bold: true, children: headerLabel })] }), _jsx(Text, { color: itemTheme.text, bold: true, wrap: "wrap", children: names.join(", ") }), item.showSetupHint && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: itemTheme.textMuted, children: "Tip: run " }), _jsx(Text, { color: itemTheme.language, bold: true, children: "/setup" }), _jsx(Text, { color: itemTheme.textMuted, children: " to audit this project against the active pack(s)" })] }) }))] }) }));
}
function renderSetupHintLive(itemTheme) {
    return (_jsx(Box, { paddingLeft: 1, marginTop: 1, flexShrink: 1, children: _jsxs(Box, { flexShrink: 1, flexDirection: "column", borderStyle: "round", borderColor: itemTheme.language, paddingX: 1, children: [_jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: itemTheme.language, bold: true, children: "◆ " }), _jsx(Text, { color: itemTheme.language, bold: true, children: "NO STYLE PACKS DETECTED" })] }), _jsx(Text, { color: itemTheme.textMuted, wrap: "wrap", children: "This directory has no recognized language manifest at its root." }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: itemTheme.textMuted, children: "Tip: run " }), _jsx(Text, { color: itemTheme.language, bold: true, children: "/setup" }), _jsx(Text, { color: itemTheme.textMuted, children: " to audit project hygiene or bootstrap a new project from scratch" })] }) })] }) }));
}
function renderUpdateNoticeLive(_item, itemTheme) {
    return (_jsx(Box, { paddingLeft: 1, marginTop: 1, flexShrink: 1, children: _jsx(Box, { flexShrink: 1, borderStyle: "round", borderColor: itemTheme.commandColor, paddingX: 1, children: _jsx(Text, { color: itemTheme.commandColor, bold: true, wrap: "wrap", children: UPDATE_NOTICE_TEXT }) }) }));
}
function assertNever(value) {
    throw new Error(`Unhandled completed item in TUI parity test: ${JSON.stringify(value)}`);
}
function liveElementFor(item) {
    switch (item.kind) {
        case "banner":
            return (_jsx(Banner, { version: context.version, model: context.model, provider: context.provider, cwd: context.cwd }));
        case "user":
            return (_jsx(UserMessage, { text: item.text, imageCount: item.imageCount, videoCount: item.videoCount, pasteInfo: item.pasteInfo }));
        case "assistant":
            return (_jsx(AssistantMessage, { text: item.text, thinking: item.thinking, thinkingMs: item.thinkingMs }));
        case "ideal_hook":
            return _jsx(IdealHookMessage, { text: item.text });
        case "task":
            return renderStatusLive("▸ ", _jsxs(_Fragment, { children: [_jsx(Text, { color: theme.textDim, children: "Task: " }), _jsx(Text, { color: theme.commandColor, bold: true, children: item.title })] }), theme.commandColor, theme, { bold: true });
        case "queued":
            return renderQueuedLiveItem(item, theme);
        case "tool_start":
            return (_jsx(ToolExecution, { status: "running", name: item.name, args: item.args, progressOutput: item.progressOutput, animateUntil: 0 }));
        case "tool_done":
            return (_jsx(ToolExecution, { status: "done", name: item.name, args: item.args, result: item.result, isError: item.isError, details: item.details }));
        case "tool_group":
            return _jsx(ToolGroupExecution, { tools: item.tools });
        case "server_tool_start":
            return (_jsx(ServerToolExecution, { status: "running", name: item.name, input: item.input, startedAt: 0, animateUntil: 0 }));
        case "server_tool_done":
            return (_jsx(ServerToolExecution, { status: "done", name: item.name, input: item.input, durationMs: item.durationMs, resultType: item.resultType }));
        case "subagent_group":
            return _jsx(SubAgentPanel, { agents: item.agents, aborted: item.aborted });
        case "style_pack":
            return renderStylePackLive(item, theme);
        case "setup_hint":
            return renderSetupHintLive(theme);
        case "update_notice":
            return renderUpdateNoticeLive(item, theme);
        case "compacting":
            return _jsx(CompactionSpinner, { staticDisplay: true });
        case "compacted":
            return (_jsx(CompactionDone, { originalCount: item.originalCount, newCount: item.newCount, tokensBefore: item.tokensBefore, tokensAfter: item.tokensAfter }));
        case "error": {
            const showMessage = item.message && item.message !== item.headline;
            return (_jsxs(Box, { flexDirection: "row", paddingLeft: 1, marginTop: 1, flexShrink: 1, children: [_jsx(Box, { width: 2, flexShrink: 0, children: _jsx(Text, { color: theme.error, bold: true, children: "✗ " }) }), _jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Text, { color: theme.error, wrap: "wrap", children: item.headline }), showMessage && (_jsx(Text, { color: theme.textDim, wrap: "wrap", children: item.message })), _jsx(Text, { color: theme.textDim, wrap: "wrap", children: `→ ${item.guidance}` })] })] }));
        }
        case "info":
            return renderStatusLive("○ ", item.text, theme.commandColor, theme, { muted: true });
        case "plan_transition":
            return _jsx(PlanModeLogo, {});
        case "model_transition":
            return renderStatusLive("▸ ", _jsxs(_Fragment, { children: [_jsx(Text, { color: theme.textDim, children: "Switched to " }), _jsx(Text, { color: theme.commandColor, bold: true, children: item.modelName })] }), theme.commandColor, theme, { bold: true });
        case "theme_transition":
            return renderStatusLive("◐ ", _jsxs(_Fragment, { children: [_jsx(Text, { color: theme.textDim, children: "Theme switched to " }), _jsx(Text, { color: theme.commandColor, bold: true, children: item.themeName })] }), theme.commandColor, theme, { bold: true });
        case "plan_event": {
            const label = item.event === "approved"
                ? "Plan approved"
                : item.event === "rejected"
                    ? "Plan rejected"
                    : "Plan dismissed";
            return renderStatusLive("○ ", _jsxs(_Fragment, { children: [_jsx(Text, { children: label }), item.detail ? _jsx(Text, { color: theme.textDim, children: ` — "${item.detail}"` }) : null] }), theme.commandColor, theme, { bold: true });
        }
        case "stopped":
            return renderStatusLive("⊘ ", item.text.replace(/\\n/g, "\n").replace(/^\n+|\n+$/g, ""), theme.commandColor, theme, { bold: true });
        case "step_done":
            return (_jsx(Box, { paddingLeft: 1, marginTop: 1, flexShrink: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.success, bold: true, children: "✓ " }), _jsx(Text, { color: theme.success, bold: true, children: `Step ${item.stepNum} done` }), item.description ? (_jsx(Text, { color: theme.textDim, children: ` — ${item.description}` })) : null] }) }));
        case "duration":
            return (_jsx(Box, { paddingLeft: 1, marginTop: 1, children: _jsx(Text, { color: theme.textDim, children: `✻ ${item.verb} 2m 3s` }) }));
        case "session_summary":
            return _jsx(SessionSummaryDisplay, { summary: item.summary });
        case "tombstone":
            return null;
        default:
            return assertNever(item);
    }
}
const parityCaseByKind = {
    banner: { kind: "banner", id: "banner" },
    user: { kind: "user", id: "user-1", text: "hello from user", imageCount: 1 },
    assistant: { kind: "assistant", id: "assistant-1", text: "Hello **world** from assistant" },
    ideal_hook: {
        kind: "ideal_hook",
        id: "ideal-hook-1",
        text: "Hook engaged \u2014 running an ideal review before finalizing.",
    },
    task: { kind: "task", id: "task-1", title: "Restore task pane" },
    queued: { kind: "queued", id: "queued-1", text: "next prompt with wrapping words" },
    tool_start: {
        kind: "tool_start",
        id: "read-start",
        toolCallId: "read-start",
        name: "read",
        args: { file_path: "src/a.ts" },
        startedAt: 0,
        animateUntil: 0,
    },
    tool_done: {
        kind: "tool_done",
        id: "read-done",
        name: "read",
        args: { file_path: "src/a.ts" },
        result: "1\tconst a = 1;",
        isError: false,
        durationMs: 1000,
    },
    tool_group: {
        kind: "tool_group",
        id: "tool-group",
        tools: [
            {
                toolCallId: "read-1",
                name: "read",
                args: { file_path: "src/a.ts" },
                status: "done",
                result: "ok",
            },
        ],
    },
    server_tool_start: {
        kind: "server_tool_start",
        id: "server-start",
        serverToolCallId: "server-start",
        name: "web_search",
        input: { query: "latest docs" },
        startedAt: 0,
        animateUntil: 0,
    },
    server_tool_done: {
        kind: "server_tool_done",
        id: "server-done",
        name: "web_search",
        input: { query: "latest docs" },
        resultType: "search_result",
        data: {},
        durationMs: 2400,
    },
    subagent_group: {
        kind: "subagent_group",
        id: "subagent",
        agents: [
            {
                toolCallId: "agent-1",
                agentName: "bee",
                task: "Inspect widgets",
                status: "done",
                toolUseCount: 2,
                tokenUsage: { input: 1200, output: 300 },
                durationMs: 1800,
            },
        ],
    },
    style_pack: {
        kind: "style_pack",
        id: "style-pack",
        added: ["typescript"],
        showSetupHint: true,
    },
    setup_hint: { kind: "setup_hint", id: "setup-hint" },
    update_notice: {
        kind: "update_notice",
        id: "update-notice",
        text: "Ken just pushed a fresh update — 4.3.214 → 4.3.215! I'll grab it on next launch (or run npm install -g @kenkaiiii/ggcoder@latest if you can't wait).",
    },
    compacting: { kind: "compacting", id: "compacting" },
    compacted: {
        kind: "compacted",
        id: "compacted",
        originalCount: 279,
        newCount: 39,
        tokensBefore: 184000,
        tokensAfter: 26000,
    },
    error: {
        kind: "error",
        id: "error",
        headline: "Provider returned an error.",
        message: "Rate limit exceeded.",
        guidance: "Retry after a moment.",
    },
    info: { kind: "info", id: "info", text: "Configuration saved" },
    plan_transition: { kind: "plan_transition", id: "plan", text: "Plan mode ON", active: true },
    model_transition: { kind: "model_transition", id: "model", modelName: "Claude Sonnet" },
    theme_transition: { kind: "theme_transition", id: "theme", themeName: "dark" },
    plan_event: { kind: "plan_event", id: "plan-event", event: "approved", detail: "ship it" },
    stopped: { kind: "stopped", id: "stopped", text: "Stopped" },
    step_done: { kind: "step_done", id: "step-done", stepNum: 2, description: "Wire final renderer" },
    duration: {
        kind: "duration",
        id: "duration",
        durationMs: 123000,
        toolsUsed: ["bash"],
        verb: "Executed commands for",
    },
    session_summary: {
        kind: "session_summary",
        id: "session-summary",
        summary: {
            title: "GG Coder is powering down. Goodbye!",
            sessionId: "session.jsonl",
            provider: "anthropic",
            model: "claude-sonnet-4-5",
            cwd: "/tmp/project",
            wallDurationMs: 123000,
            turns: 2,
            usage: { inputTokens: 1000, outputTokens: 250, cacheRead: 50 },
            tools: {
                totalCalls: 2,
                totalSuccess: 1,
                totalFail: 1,
                totalDurationMs: 3000,
                byName: { bash: { calls: 2, success: 1, fail: 1, durationMs: 3000 } },
            },
            serverToolCalls: 0,
            linesChanged: { added: 3, removed: 1 },
            footer: "To resume this session: ggcoder --resume session.jsonl",
        },
    },
};
const supplementalParityCases = [
    {
        kind: "tool_start",
        id: "bash-start",
        toolCallId: "bash-start",
        name: "bash",
        args: { command: "printf hi" },
        progressOutput: "first line\nsecond line",
        startedAt: 0,
        animateUntil: 0,
    },
    {
        kind: "tool_done",
        id: "bash-done",
        name: "bash",
        args: { command: "printf hi" },
        result: "Exit code: 0\nhi",
        isError: false,
        durationMs: 1000,
    },
    {
        kind: "tool_start",
        id: "mcp-search-code-start",
        toolCallId: "mcp-search-code-start",
        name: "mcp__kencode-search__searchCode",
        args: { query: "useState(", language: ["TypeScript"] },
        startedAt: 0,
        animateUntil: 0,
    },
    {
        kind: "tool_done",
        id: "mcp-search-code-done",
        name: "mcp__kencode-search__searchCode",
        args: { query: "useState(", language: ["TypeScript"] },
        result: 'Repo: owner/project (★123, MIT)\nFile: src/App.tsx\nLink: https://github.com/owner/project/blob/main/src/App.tsx\n\n12 │ const [value, setValue] = useState("");',
        isError: false,
        durationMs: 1000,
    },
];
const parityCases = [...Object.values(parityCaseByKind), ...supplementalParityCases];
describe("TUI live/history parity", () => {
    it.each(parityCases.map((item) => [item.kind, item]))("keeps %s live rendering aligned with terminal history", (_kind, item) => {
        const liveElement = liveElementFor(item);
        expect(liveElement).not.toBeNull();
        const liveLines = renderLive(liveElement);
        const historyLines = renderHistory(item);
        expect(normalizeParityLines(item.kind, liveLines), item.kind).toEqual(normalizeParityLines(item.kind, historyLines));
        for (const line of liveLines) {
            expect(stringWidth(line), `${item.kind}: ${line}`).toBeLessThanOrEqual(TERMINAL_COLUMNS);
        }
    });
});
//# sourceMappingURL=tui-history-parity.test.js.map