/**
 * Stream display manager for real-time Claude/Codex output.
 *
 * Handles text, thinking, tool use/result events and renders them
 * to the terminal with appropriate formatting and spinners.
 */
import chalk from 'chalk';
import { truncate } from './LogManager.js';
import { stripAnsi } from '../utils/text.js';
/** Stream display manager for real-time Claude output */
export class StreamDisplay {
    agentName;
    quiet;
    progressInfo;
    lastToolUse = null;
    currentToolInputPreview = null;
    toolOutputBuffer = '';
    toolOutputPrinted = false;
    textBuffer = '';
    thinkingBuffer = '';
    isFirstText = true;
    isFirstThinking = true;
    toolSpinner = null;
    spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
    spinnerFrame = 0;
    constructor(agentName, quiet, progressInfo) {
        this.agentName = agentName;
        this.quiet = quiet;
        this.progressInfo = progressInfo;
    }
    /**
     * Build progress prefix string for display.
     * Format: `(iteration/maxSteps) step stepIndex/totalSteps`
     * Example: `(3/10) step 2/4`
     */
    buildProgressPrefix() {
        if (!this.progressInfo) {
            return '';
        }
        const { iteration, maxSteps, stepIndex, totalSteps } = this.progressInfo;
        return `(${iteration}/${maxSteps}) step ${stepIndex + 1}/${totalSteps}`;
    }
    showInit(model) {
        if (this.quiet)
            return;
        const progress = this.buildProgressPrefix();
        const progressPart = progress ? ` ${progress}` : '';
        console.log(chalk.gray(`[${this.agentName}]${progressPart} Model: ${model}`));
    }
    startToolSpinner(tool, inputPreview) {
        this.stopToolSpinner();
        const message = `${chalk.yellow(tool)} ${chalk.gray(inputPreview)}`;
        this.toolSpinner = {
            intervalId: setInterval(() => {
                const frame = this.spinnerFrames[this.spinnerFrame];
                this.spinnerFrame = (this.spinnerFrame + 1) % this.spinnerFrames.length;
                process.stdout.write(`\r  ${chalk.cyan(frame)} ${message}`);
            }, 80),
            toolName: tool,
            message,
        };
    }
    stopToolSpinner() {
        if (this.toolSpinner) {
            clearInterval(this.toolSpinner.intervalId);
            process.stdout.write('\r' + ' '.repeat(120) + '\r');
            this.toolSpinner = null;
            this.spinnerFrame = 0;
        }
    }
    showToolUse(tool, input) {
        if (this.quiet)
            return;
        this.flushText();
        const inputPreview = this.formatToolInput(tool, input);
        // Starting a spinner would corrupt the interactive prompt output.
        if (tool !== 'AskUserQuestion') {
            this.startToolSpinner(tool, inputPreview);
        }
        this.lastToolUse = tool;
        this.currentToolInputPreview = inputPreview;
        this.toolOutputBuffer = '';
        this.toolOutputPrinted = false;
    }
    showToolOutput(output, tool) {
        if (this.quiet)
            return;
        if (!output)
            return;
        this.stopToolSpinner();
        this.flushThinking();
        this.flushText();
        if (tool && !this.lastToolUse) {
            this.lastToolUse = tool;
        }
        this.toolOutputBuffer += stripAnsi(output);
        const lines = this.toolOutputBuffer.split(/\r?\n/);
        this.toolOutputBuffer = lines.pop() ?? '';
        this.printToolOutputLines(lines, tool);
        if (this.lastToolUse && this.currentToolInputPreview) {
            this.startToolSpinner(this.lastToolUse, this.currentToolInputPreview);
        }
    }
    showToolResult(content, isError) {
        this.stopToolSpinner();
        const sanitizedContent = stripAnsi(content);
        if (this.quiet) {
            if (isError) {
                const toolName = this.lastToolUse || 'Tool';
                const errorContent = sanitizedContent || 'Unknown error';
                console.log(chalk.red(`  ✗ ${toolName}:`), chalk.red(truncate(errorContent, 70)));
            }
            this.lastToolUse = null;
            this.currentToolInputPreview = null;
            this.toolOutputPrinted = false;
            return;
        }
        if (this.toolOutputBuffer) {
            this.printToolOutputLines([this.toolOutputBuffer], this.lastToolUse ?? undefined);
            this.toolOutputBuffer = '';
        }
        const toolName = this.lastToolUse || 'Tool';
        if (isError) {
            const errorContent = sanitizedContent || 'Unknown error';
            console.log(chalk.red(`  ✗ ${toolName}:`), chalk.red(truncate(errorContent, 70)));
        }
        else if (toolName === 'AskUserQuestion') {
            // SDK content preview includes misleading "Error:" text for successful responses.
            console.log(chalk.green(`  ✓ ${toolName}`));
        }
        else if (sanitizedContent && sanitizedContent.length > 0) {
            const preview = sanitizedContent.split('\n')[0] || sanitizedContent;
            console.log(chalk.green(`  ✓ ${toolName}`), chalk.gray(truncate(preview, 60)));
        }
        else {
            console.log(chalk.green(`  ✓ ${toolName}`));
        }
        this.lastToolUse = null;
        this.currentToolInputPreview = null;
        this.toolOutputPrinted = false;
    }
    showThinking(thinking) {
        if (this.quiet)
            return;
        this.stopToolSpinner();
        this.flushText();
        if (this.isFirstThinking) {
            console.log();
            const progress = this.buildProgressPrefix();
            const progressPart = progress ? ` ${progress}` : '';
            console.log(chalk.magenta(`💭 [${this.agentName}]${progressPart} thinking:`));
            this.isFirstThinking = false;
        }
        const sanitized = stripAnsi(thinking);
        process.stdout.write(chalk.gray.italic(sanitized));
        this.thinkingBuffer += sanitized;
    }
    flushThinking() {
        if (this.thinkingBuffer) {
            if (!this.thinkingBuffer.endsWith('\n')) {
                console.log();
            }
            this.thinkingBuffer = '';
            this.isFirstThinking = true;
        }
    }
    showText(text) {
        if (this.quiet)
            return;
        this.stopToolSpinner();
        this.flushThinking();
        if (this.isFirstText) {
            console.log();
            const progress = this.buildProgressPrefix();
            const progressPart = progress ? ` ${progress}` : '';
            console.log(chalk.cyan(`[${this.agentName}]${progressPart}:`));
            this.isFirstText = false;
        }
        const sanitized = stripAnsi(text);
        process.stdout.write(sanitized);
        this.textBuffer += sanitized;
    }
    flushText() {
        if (this.textBuffer) {
            if (!this.textBuffer.endsWith('\n')) {
                console.log();
            }
            this.textBuffer = '';
            this.isFirstText = true;
        }
    }
    flush() {
        this.stopToolSpinner();
        this.flushThinking();
        this.flushText();
    }
    showResult(success, error) {
        this.stopToolSpinner();
        this.flushThinking();
        this.flushText();
        console.log();
        if (success) {
            console.log(chalk.green('✓ Complete'));
        }
        else {
            console.log(chalk.red('✗ Failed'));
            if (error) {
                console.log(chalk.red(`  ${error}`));
            }
        }
    }
    reset() {
        this.stopToolSpinner();
        this.lastToolUse = null;
        this.currentToolInputPreview = null;
        this.toolOutputBuffer = '';
        this.toolOutputPrinted = false;
        this.textBuffer = '';
        this.thinkingBuffer = '';
        this.isFirstText = true;
        this.isFirstThinking = true;
    }
    createHandler() {
        return (event) => {
            switch (event.type) {
                case 'init':
                    this.showInit(event.data.model);
                    break;
                case 'tool_use':
                    this.showToolUse(event.data.tool, event.data.input);
                    break;
                case 'tool_result':
                    this.showToolResult(event.data.content, event.data.isError);
                    break;
                case 'tool_output':
                    this.showToolOutput(event.data.output, event.data.tool);
                    break;
                case 'text':
                    this.showText(event.data.text);
                    break;
                case 'thinking':
                    this.showThinking(event.data.thinking);
                    break;
                case 'result':
                    this.showResult(event.data.success, event.data.error);
                    break;
                case 'assistant_error':
                case 'rate_limit':
                case 'error':
                    break;
            }
        };
    }
    formatToolInput(tool, input) {
        switch (tool) {
            case 'Bash':
                return truncate(String(input.command || ''), 60);
            case 'Read':
                return truncate(String(input.file_path || ''), 60);
            case 'Write':
            case 'Edit':
                return truncate(String(input.file_path || ''), 60);
            case 'Glob':
                return truncate(String(input.pattern || ''), 60);
            case 'Grep':
                return truncate(String(input.pattern || ''), 60);
            default: {
                const keys = Object.keys(input);
                if (keys.length === 0)
                    return '';
                const firstKey = keys[0];
                if (firstKey) {
                    const value = input[firstKey];
                    return truncate(String(value || ''), 50);
                }
                return '';
            }
        }
    }
    ensureToolOutputHeader(tool) {
        if (this.toolOutputPrinted)
            return;
        const label = tool || this.lastToolUse || 'Tool';
        console.log(chalk.gray(`  ${chalk.yellow(label)} output:`));
        this.toolOutputPrinted = true;
    }
    printToolOutputLines(lines, tool) {
        if (lines.length === 0)
            return;
        this.ensureToolOutputHeader(tool);
        for (const line of lines) {
            console.log(chalk.gray(`  │ ${line}`));
        }
    }
}
//# sourceMappingURL=StreamDisplay.js.map