import { Spacer, TUI } from "@earendil-works/pi-tui";
import { resolveThemeTokensById, } from "../core/config/index.js";
import { createAppTerminal } from "./terminal.js";
import { ToolUiRouter } from "./tool_ui_router.js";
import { ChatContainerComponent } from "./ui/chat_container.js";
import { CustomEditor } from "./ui/custom_editor.js";
import { FooterComponent } from "./ui/footer.js";
import { QueuedMessagesComponent } from "./ui/queued_messages.js";
import { RewindPickerComponent } from "./ui/rewind_picker.js";
import { SubagentEditorPaneComponent } from "./ui/subagent_editor_pane.js";
import { SubagentPanelComponent } from "./ui/subagent_panel.js";
import { coercePaletteOverrides, createUiTheme } from "./ui/theme/index.js";
import { createToolUiRegistry } from "./ui/tool_ui_registry.js";
export class TuiChatView {
    ui;
    chatContainer;
    footer;
    queuedMessages;
    subagentPanel;
    editor;
    editorPane;
    rewindPicker;
    activeInputPane;
    uiTheme;
    terminalAppearance;
    toolUiRegistry = createToolUiRegistry();
    toolUiRouter;
    lastStatus;
    recordingIndicatorFrame = 0;
    recordingStartedAt;
    recordingIndicatorTimer;
    constructor(options) {
        this.terminalAppearance = options.terminalAppearance ?? "dark";
        const themeTokens = resolveThemeTokensById(options.themeId, options.themes, this.terminalAppearance);
        const paletteOverrides = coercePaletteOverrides(themeTokens);
        this.uiTheme = createUiTheme("ansi", paletteOverrides);
        this.ui = new TUI(createAppTerminal());
        this.chatContainer = new ChatContainerComponent(this.uiTheme, this.toolUiRegistry, options.showThinking);
        this.chatContainer.setCompactToolUi(options.compactToolUi);
        this.footer = new FooterComponent(this.uiTheme, this.ui);
        this.queuedMessages = new QueuedMessagesComponent(this.uiTheme, options.queuedUserMessages);
        this.subagentPanel = new SubagentPanelComponent(this.uiTheme);
        this.editor = new CustomEditor(this.uiTheme);
        this.editor.onUiChange = () => this.ui.requestRender();
        this.editorPane = new SubagentEditorPaneComponent(this.uiTheme, this.subagentPanel, this.editor);
        this.activeInputPane = this.editorPane;
        this.toolUiRouter = new ToolUiRouter({
            chatContainer: this.chatContainer,
            requestRender: () => this.ui.requestRender(),
        });
        this.setupUi();
    }
    start() {
        this.ui.start();
    }
    stop() {
        this.setRecordingIndicatorActive(false);
        this.ui.stop();
        // Ensure cursor is visible after shutdown (some terminals keep it hidden).
        this.ui.terminal.showCursor();
    }
    requestRender() {
        this.ui.requestRender();
    }
    removeMessages(ids) {
        this.chatContainer.removeMessages(ids);
        this.ui.requestRender();
    }
    removeMessagesFrom(id) {
        this.chatContainer.removeMessagesFrom(id);
        this.ui.requestRender();
    }
    addMessage(model, id) {
        const messageId = this.chatContainer.addMessage(model, id);
        this.ui.requestRender();
        return messageId;
    }
    replaceMessage(id, model) {
        this.chatContainer.replaceMessage(id, model);
        this.ui.requestRender();
    }
    updateMessage(id, model) {
        this.chatContainer.updateMessage(id, model);
        this.ui.requestRender();
    }
    updateAssistantMessage(id, model) {
        this.chatContainer.updateMessage(id, model);
        this.ui.requestRender();
    }
    addSystemMessage(text, kind, options) {
        const cleanedText = this.normalizeSystemMessageText(text, kind);
        const toastText = this.formatToastText(cleanedText);
        if (kind !== "muted" && toastText.length > 0) {
            this.footer.showToast(toastText, kind, options?.toastDurationMs);
        }
        const shouldPersist = options?.persist ?? this.shouldPersistSystemMessage(cleanedText, kind);
        if (shouldPersist) {
            this.chatContainer.addMessage({ type: "system", text: cleanedText, kind });
            this.ui.requestRender();
        }
    }
    setThinkingVisibility(show) {
        this.chatContainer.setThinkingVisibility(show);
        this.ui.requestRender();
    }
    setCompactToolUi(compact) {
        this.chatContainer.setCompactToolUi(compact);
        this.ui.requestRender();
    }
    updateStatus(status) {
        this.lastStatus = status;
        this.setRecordingIndicatorActive(status.editor.mode === "recording");
        this.footer.setStatus({
            contextUsage: status.footer.contextUsage,
            sessionCost: status.footer.sessionCost,
            duration: status.footer.duration,
            riskLevel: status.footer.riskLevel,
            commandHint: status.footer.commandHint,
        });
        this.updateEditorVisualState(status.editor);
        this.ui.requestRender();
    }
    startWorkingIcon() {
        this.footer.startWorkingIcon();
    }
    stopWorkingIcon() {
        this.footer.stop();
    }
    handleToolUiEvent(event) {
        this.toolUiRouter.handle(event);
    }
    handleSubagentEvent(event) {
        this.subagentPanel.handleEvent(event);
        this.ui.requestRender();
    }
    resetToolUiSession() {
        this.toolUiRouter.resetSession();
        this.subagentPanel.reset();
    }
    resetToolUiSessionPreservingSubagents() {
        this.toolUiRouter.resetSession();
    }
    finalizeToolUiPending(reason) {
        this.toolUiRouter.finalizePending(reason);
    }
    clearToolUiTransientState() {
        this.toolUiRouter.clearTransientState();
    }
    getToolUiCostTotal() {
        return this.subagentPanel.getCostTotal();
    }
    cycleSubagentSelection(direction) {
        const selected = this.subagentPanel.cycleSelection(direction);
        this.ui.requestRender();
        return selected;
    }
    getSelectedSubagentId() {
        return this.subagentPanel.getSelectedId();
    }
    sanitizeOscText(text) {
        return Array.from(text)
            .filter((ch) => {
            const code = ch.codePointAt(0);
            return code !== undefined && code > 0x1f && code !== 0x7f;
        })
            .join("");
    }
    sendTerminalNotification(title) {
        if (!process.stdout.isTTY)
            return;
        const safeTitle = this.sanitizeOscText(title);
        this.ui.terminal.write(`\x1b]9;${safeTitle}\x1b\\`);
    }
    getEditorText() {
        return this.editor.getText();
    }
    getExpandedEditorText() {
        return this.editor.getExpandedText();
    }
    setEditorText(text) {
        this.editor.setText(text);
        this.ui.requestRender();
    }
    insertEditorTextAtCursor(text) {
        this.editor.insertTextAtCursor(text);
        this.ui.requestRender();
    }
    setEditorInputEnabled(enabled) {
        this.editor.setInputEnabled(enabled);
        this.ui.requestRender();
    }
    showRewindPicker(options) {
        const picker = new RewindPickerComponent(this.uiTheme, options.items);
        picker.onSelect = options.onSelect;
        picker.onCancel = options.onCancel;
        this.rewindPicker = picker;
        this.activeInputPane = picker;
        this.renderLayout();
        this.ui.setFocus(picker);
        this.ui.requestRender();
    }
    hideRewindPicker() {
        if (!this.rewindPicker)
            return;
        this.rewindPicker = undefined;
        this.activeInputPane = this.editorPane;
        this.renderLayout();
        this.ui.setFocus(this.editor);
        this.ui.requestRender();
    }
    getEditorCursor() {
        return this.editor.getCursor();
    }
    getEditorLines() {
        return this.editor.getLines();
    }
    bindInputHandlers(handlers) {
        this.editor.onCtrlC = handlers.onCtrlC;
        this.editor.onCtrlT = handlers.onCtrlT;
        this.editor.onCtrlO = handlers.onCtrlO;
        this.editor.onShiftTab = handlers.onShiftTab;
        this.editor.onCtrlR = handlers.onCtrlR;
        this.editor.onCtrlP = handlers.onCtrlP;
        this.editor.onCtrlS = handlers.onCtrlS;
        this.editor.onCtrlY = handlers.onCtrlY;
        this.editor.onEscape = handlers.onEscape;
        this.editor.onCtrlF = handlers.onCtrlF;
        this.editor.onAltUp = handlers.onAltUp;
        this.editor.onAltDown = handlers.onAltDown;
        this.editor.onCtrlG = handlers.onCtrlG;
        this.editor.beforeSubmit = handlers.beforeSubmit;
        this.editor.onChange = handlers.onChange;
        this.editor.onSubmit = handlers.onSubmit;
    }
    setAutocompleteProvider(provider) {
        this.editor.setAutocompleteProvider(provider);
    }
    addBashExecutionMessage(args) {
        const toolCallId = `bash-user-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
        const headerTarget = args.command.split(/\r?\n/)[0] ?? args.command;
        const event = {
            type: "bash_execution",
            toolCallId,
            command: args.command,
            headerTarget,
            exitCode: args.exitCode,
            truncationInfo: args.truncationInfo,
            uiText: args.uiText,
            durationMs: args.durationMs,
            labelOverride: args.labelOverride,
        };
        this.chatContainer.addMessage({ type: "tool", event });
        this.ui.requestRender();
    }
    updateTheme(options) {
        const themeTokens = resolveThemeTokensById(options.themeId, options.themes, this.terminalAppearance);
        const paletteOverrides = coercePaletteOverrides(themeTokens);
        this.uiTheme = createUiTheme("ansi", paletteOverrides);
        this.chatContainer.setTheme(this.uiTheme);
        this.footer.setTheme(this.uiTheme);
        this.queuedMessages.setTheme(this.uiTheme);
        this.subagentPanel.setTheme(this.uiTheme);
        this.editor.setUiTheme(this.uiTheme);
        this.editorPane.setTheme(this.uiTheme);
        this.rewindPicker?.setTheme(this.uiTheme);
        if (this.lastStatus) {
            this.updateEditorVisualState(this.lastStatus.editor);
        }
        this.ui.invalidate();
        this.ui.requestRender(true);
    }
    setupUi() {
        this.renderLayout();
        this.ui.setFocus(this.editor);
    }
    renderLayout() {
        this.ui.clear();
        this.ui.addChild(this.chatContainer);
        this.ui.addChild(new Spacer(1));
        this.ui.addChild(this.queuedMessages);
        this.ui.addChild(this.activeInputPane);
        this.ui.addChild(this.footer);
    }
    updateEditorVisualState(state) {
        const { palette } = this.uiTheme;
        if (state.mode === "bash" || state.mode === "bash_incognito") {
            this.editor.borderColor = (s) => palette.modeBash(s);
        }
        else if (state.mode === "memory") {
            this.editor.borderColor = (s) => palette.modeMemory(s);
        }
        else if (state.mode === "recording") {
            this.editor.borderColor = (s) => palette.editorBorderRecording(s);
        }
        else {
            this.editor.borderColor = this.uiTheme.editorBorderForReasoning(state.reasoning);
        }
        if (state.mode === "bash" || state.mode === "bash_incognito") {
            const label = state.mode === "bash_incognito" ? "bash incognito" : "bash";
            this.editor.setHeader(label, "", { leftStyle: this.editor.borderColor });
            return;
        }
        if (state.mode === "memory") {
            this.editor.setHeader("memoize", "", { leftStyle: this.editor.borderColor });
            return;
        }
        if (state.mode === "recording") {
            this.editor.setHeader(`${this.getRecordingIndicator()} recording (${this.getRecordingDurationLabel()})`, "", {
                leftStyle: this.editor.borderColor,
            });
            return;
        }
        this.editor.setHeader(state.cwdLabel, `${state.personaName} (${state.reasoningLabel})`);
    }
    setRecordingIndicatorActive(active) {
        if (!active) {
            if (this.recordingIndicatorTimer) {
                clearInterval(this.recordingIndicatorTimer);
                this.recordingIndicatorTimer = undefined;
            }
            this.recordingIndicatorFrame = 0;
            this.recordingStartedAt = undefined;
            return;
        }
        if (this.recordingIndicatorTimer) {
            return;
        }
        this.recordingIndicatorFrame = 0;
        this.recordingStartedAt = Date.now();
        this.recordingIndicatorTimer = setInterval(() => {
            if (!this.lastStatus || this.lastStatus.editor.mode !== "recording") {
                return;
            }
            this.recordingIndicatorFrame = (this.recordingIndicatorFrame + 1) % 2;
            this.updateEditorVisualState(this.lastStatus.editor);
            this.ui.requestRender();
        }, 500);
    }
    getRecordingIndicator() {
        return this.recordingIndicatorFrame === 0 ? "●" : "○";
    }
    getRecordingDurationLabel() {
        if (!this.recordingStartedAt) {
            return "00:00";
        }
        const elapsedSeconds = Math.max(0, Math.floor((Date.now() - this.recordingStartedAt) / 1000));
        const minutes = Math.floor(elapsedSeconds / 60);
        const seconds = elapsedSeconds % 60;
        return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
    }
    normalizeSystemMessageText(text, kind) {
        const cleaned = kind !== "error" ? text : text.replace(/^\s*error:\s*/i, "");
        return this.stripTrailingPunctuation(cleaned);
    }
    stripTrailingPunctuation(text) {
        const trimmed = text.replace(/\s+$/, "");
        if (!trimmed)
            return trimmed;
        return trimmed.replace(/[.!?…,:;]+$/, "");
    }
    formatToastText(text) {
        const firstLine = text.split(/\r?\n/, 1)[0] ?? "";
        return firstLine.replace(/\s+/g, " ").trim();
    }
    shouldPersistSystemMessage(text, kind) {
        if (kind === "muted" || kind === "error")
            return true;
        if (text.includes("\n"))
            return true;
        return text.length > 140;
    }
}
//# sourceMappingURL=chat_view.js.map