/**
 * PromptController - minimal wrapper around UnifiedUIRenderer for coding agent UX.
 * Handles input wiring and status/meta updates without any of the legacy scroll-region plumbing.
 */
import { UnifiedUIRenderer } from './UnifiedUIRenderer.js';
export class PromptController {
    renderer;
    callbacks;
    editMode = 'display-edits';
    statusMain = null;
    statusOverride = null;
    statusStreaming = null;
    modeToggleState = {
        autoMode: 'off',
        autoContinueHotkey: '⌥G',
        thinkingModeLabel: 'balanced',
        thinkingHotkey: '⌥T',
        criticalApprovalMode: 'auto',
        criticalApprovalHotkey: '⌥A',
    };
    started = false;
    disposed = false;
    // Store bound handlers for cleanup
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    boundHandlers = new Map();
    constructor(inStream, outStream, callbacks, existingRenderer) {
        this.callbacks = callbacks;
        this.renderer = existingRenderer ?? new UnifiedUIRenderer(outStream, inStream);
        // Create bound handlers that can be removed later
        this.addBoundHandler('submit', (text) => this.callbacks.onSubmit(text));
        this.addBoundHandler('queue', (text) => this.callbacks.onQueue(text));
        this.addBoundHandler('interrupt', () => this.callbacks.onInterrupt());
        this.addBoundHandler('ctrlc', (info) => this.callbacks.onCtrlC?.(info));
        this.addBoundHandler('exit', () => this.callbacks.onExit?.());
        this.addBoundHandler('change', (payload) => {
            this.callbacks.onChange?.(payload);
        });
        this.addBoundHandler('resume', () => this.callbacks.onResume?.());
        this.addBoundHandler('expand-tool-result', () => {
            const remainingCount = this.renderer.getCollapsedResultCount();
            const expanded = this.renderer.expandLastToolResult();
            if (!expanded) {
                this.setStatusMessage('No tool result to expand');
                setTimeout(() => this.setStatusMessage(null), 2000);
            }
            else if (remainingCount > 1) {
                // Show how many more results can be expanded
                this.setStatusMessage(`Expanded (${remainingCount - 1} more, ctrl+o)`);
                setTimeout(() => this.setStatusMessage(null), 2000);
            }
            this.callbacks.onExpandToolResult?.();
        });
        this.addBoundHandler('toggle-auto-continue', () => {
            // Cycle through: off → on → dual → off
            const current = this.modeToggleState.autoMode;
            this.modeToggleState.autoMode = current === 'off' ? 'on' : current === 'on' ? 'dual' : 'off';
            this.syncModeToggles();
            this.callbacks.onToggleAutoContinue?.();
        });
        this.addBoundHandler('toggle-critical-approval', () => {
            this.modeToggleState.criticalApprovalMode =
                this.modeToggleState.criticalApprovalMode === 'auto' ? 'approval' : 'auto';
            this.syncModeToggles();
            this.callbacks.onToggleCriticalApproval?.();
        });
        this.addBoundHandler('toggle-thinking', () => {
            // Cycle between 'balanced' and 'deep'
            const current = (this.modeToggleState.thinkingModeLabel || 'balanced').toLowerCase();
            this.modeToggleState.thinkingModeLabel = current === 'deep' ? 'balanced' : 'deep';
            this.syncModeToggles();
            this.callbacks.onToggleThinking?.();
        });
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    addBoundHandler(event, handler) {
        this.boundHandlers.set(event, handler);
        this.renderer.on(event, handler);
    }
    removeAllBoundHandlers() {
        for (const [event, handler] of this.boundHandlers) {
            this.renderer.removeListener(event, handler);
        }
        this.boundHandlers.clear();
    }
    getRenderer() {
        return this.renderer;
    }
    start() {
        if (this.started) {
            return;
        }
        this.started = true;
        this.renderer.initialize();
        this.syncModeToggles();
    }
    stop() {
        if (this.disposed)
            return;
        this.disposed = true;
        // Remove all event listeners before cleanup to prevent memory leaks
        this.removeAllBoundHandlers();
        this.renderer.cleanup();
        this.started = false;
    }
    setStreaming(streaming) {
        this.renderer.setMode(streaming ? 'streaming' : 'idle');
    }
    getMode() {
        return this.renderer.getMode();
    }
    setContextUsage(percentage) {
        if (percentage !== null) {
            this.renderer.updateStatusMeta({ contextPercent: percentage });
        }
    }
    setModeToggles(_options) {
        const options = _options || { autoMode: 'off' };
        this.modeToggleState = {
            ...this.modeToggleState,
            autoMode: options.autoMode ?? this.modeToggleState.autoMode,
            autoContinueHotkey: options.autoContinueHotkey,
            thinkingModeLabel: options.thinkingModeLabel,
            thinkingHotkey: options.thinkingHotkey,
            criticalApprovalMode: options.criticalApprovalMode ?? this.modeToggleState.criticalApprovalMode,
            criticalApprovalHotkey: options.criticalApprovalHotkey,
        };
        this.syncModeToggles();
    }
    setDebugMode(enabled) {
        this.modeToggleState.debugEnabled = enabled;
        this.syncModeToggles();
    }
    toggleAutoContinue() {
        // Cycle through: off → on → dual → off
        const current = this.modeToggleState.autoMode;
        this.modeToggleState.autoMode = current === 'off' ? 'on' : current === 'on' ? 'dual' : 'off';
        this.syncModeToggles();
        this.callbacks.onToggleAutoContinue?.();
    }
    /** Get the current auto mode */
    getAutoMode() {
        return this.modeToggleState.autoMode;
    }
    /** Set auto mode directly */
    setAutoMode(mode) {
        this.modeToggleState.autoMode = mode;
        this.syncModeToggles();
    }
    toggleApprovals() {
        this.modeToggleState.criticalApprovalMode =
            this.modeToggleState.criticalApprovalMode === 'auto' ? 'approval' : 'auto';
        this.syncModeToggles();
        this.callbacks.onToggleCriticalApproval?.();
    }
    toggleThinking() {
        const current = (this.modeToggleState.thinkingModeLabel || 'balanced').toLowerCase();
        this.modeToggleState.thinkingModeLabel = current === 'deep' ? 'balanced' : 'deep';
        this.syncModeToggles();
        this.callbacks.onToggleThinking?.();
    }
    /**
     * Sync the stored toggle state to the renderer so UI reflects the latest flags.
     */
    syncModeToggles() {
        this.renderer.updateModeToggles(this.modeToggleState);
    }
    getModeToggleState() {
        return this.modeToggleState;
    }
    setStatusMessage(message) {
        this.setStatusBundle({ main: message });
    }
    setOverrideStatus(message) {
        this.setStatusBundle({ override: message });
    }
    setStreamingLabel(label) {
        this.setStatusBundle({ streaming: label });
    }
    /**
     * Atomically set the streaming/override/main status text and render once.
     */
    setStatusLine(status) {
        this.setStatusBundle(status);
    }
    setMetaStatus(meta) {
        const statusMeta = {};
        if (typeof meta.elapsedSeconds === 'number' && Number.isFinite(meta.elapsedSeconds) && meta.elapsedSeconds >= 0) {
            statusMeta.sessionTime = this.formatElapsed(meta.elapsedSeconds);
        }
        if (meta.tokensUsed != null && meta.tokenLimit != null) {
            const percent = meta.tokenLimit > 0 ? Math.round((meta.tokensUsed / meta.tokenLimit) * 100) : 0;
            statusMeta.contextPercent = percent;
        }
        this.renderer.updateStatusMeta(statusMeta);
    }
    clearAllStatus() {
        this.setStatusBundle({
            main: null,
            override: null,
            streaming: null,
        });
    }
    setModelContext(options) {
        this.renderer.updateStatusMeta({
            model: options.model || undefined,
            provider: options.provider || undefined,
        }, { render: true });
    }
    setChromeMeta(meta) {
        this.renderer.updateStatusMeta(meta, { render: true });
    }
    setAvailableCommands(commands) {
        this.renderer.setAvailableCommands(commands);
    }
    /**
     * Display content in the inline panel (below prompt, above toggles).
     * Content is transient - not persisted to chat history.
     * Use for help menus, status displays, etc.
     */
    setInlinePanel(lines) {
        this.renderer.setInlinePanel(lines);
    }
    /**
     * Clear the inline panel.
     */
    clearInlinePanel() {
        this.renderer.clearInlinePanel();
    }
    /**
     * Check if inline panel is supported (TTY mode only).
     */
    supportsInlinePanel() {
        return this.renderer.supportsInlinePanel();
    }
    /**
     * Show an interactive menu with arrow key navigation (Claude Code style).
     * @param items - Menu items to display
     * @param options - Menu options (title, initialIndex)
     * @param callback - Called when user selects an item (or null if cancelled)
     */
    setMenu(items, options, callback) {
        this.renderer.setMenu(items, options, callback);
    }
    /**
     * Close the active menu without selecting anything.
     */
    closeMenu() {
        this.renderer.closeMenu();
    }
    /**
     * Check if an interactive menu is currently active.
     */
    isMenuActive() {
        return this.renderer.isMenuActive();
    }
    /**
     * Set the activity message displayed during streaming (e.g., "Reasoning", "Thinking").
     * This updates the animated status line: "✳ Reasoning… (esc to interrupt · 34s)"
     */
    setActivityMessage(message) {
        this.renderer.setActivity(message);
    }
    setEditMode(mode) {
        this.editMode = mode;
        this.callbacks.onEditModeChange?.(mode);
    }
    applyEditMode(mode) {
        this.setEditMode(mode);
    }
    getEditMode() {
        return this.editMode;
    }
    getBuffer() {
        return this.renderer.getBuffer();
    }
    getCursor() {
        return this.renderer.getCursor();
    }
    setBuffer(text, cursorPos) {
        this.renderer.setBuffer(text, cursorPos);
    }
    setSecretMode(enabled) {
        this.renderer.setSecretMode(enabled);
    }
    clear() {
        this.renderer.clearBuffer();
    }
    render() {
        this.renderer.render();
    }
    forceRender() {
        this.renderer.render();
    }
    handleResize() {
        this.renderer.render();
    }
    dispose() {
        this.renderer.cleanup();
    }
    // ------------ RL Agent Status Methods ------------
    /**
     * Update RL agent execution status for display in the UI.
     * Called during dual-RL mode to show active agent, module/step progress, and win statistics.
     */
    updateRLStatus(status) {
        this.renderer.updateRLStatus(status);
    }
    /**
     * Clear RL agent status (e.g., when RL run completes).
     */
    clearRLStatus() {
        this.renderer.clearRLStatus();
    }
    /**
     * Get current RL status for external access.
     */
    getRLStatus() {
        return this.renderer.getRLStatus();
    }
    /**
     * Update the status line with a single composed message so the UI looks identical
     * before and during streaming. The renderer only owns one status field, so we
     * join the different status sources here.
     */
    setStatusBundle(status) {
        if ('main' in status) {
            this.statusMain = status.main?.trim() || null;
        }
        if ('override' in status) {
            this.statusOverride = status.override?.trim() || null;
        }
        if ('streaming' in status) {
            this.statusStreaming = status.streaming?.trim() || null;
        }
        this.refreshStatus();
    }
    refreshStatus() {
        this.renderer.updateStatusBundle({
            main: this.statusMain,
            override: this.statusOverride,
            streaming: this.statusStreaming,
        });
    }
    formatElapsed(totalSeconds) {
        const minutes = Math.floor(totalSeconds / 60);
        const seconds = totalSeconds % 60;
        const paddedSeconds = seconds < 10 ? `0${seconds}` : `${seconds}`;
        return `${minutes}:${paddedSeconds}`;
    }
}
//# sourceMappingURL=PromptController.js.map