"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FancyRenderer = void 0;
const events_1 = require("events");
const c = {
    reset: "\x1b[0m",
    bold: "\x1b[1m",
    dim: "\x1b[2m",
    italic: "\x1b[3m",
    underline: "\x1b[4m",
    black: "\x1b[30m",
    red: "\x1b[31m",
    green: "\x1b[32m",
    yellow: "\x1b[33m",
    blue: "\x1b[34m",
    magenta: "\x1b[35m",
    cyan: "\x1b[36m",
    white: "\x1b[37m",
    gray: "\x1b[90m",
    bgBlack: "\x1b[40m",
    bgRed: "\x1b[41m",
    bgGreen: "\x1b[42m",
    bgYellow: "\x1b[43m",
    bgBlue: "\x1b[44m",
    bgMagenta: "\x1b[45m",
    bgCyan: "\x1b[46m",
    bgWhite: "\x1b[47m",
    bgGray: "\x1b[100m",
    brightBlue: "\x1b[94m",
    brightCyan: "\x1b[96m",
    brightGreen: "\x1b[92m",
    brightYellow: "\x1b[93m",
    brightMagenta: "\x1b[95m",
    brightWhite: "\x1b[97m",
};
const box = {
    tl: "╭", tr: "╮",
    bl: "╰", br: "╯",
    h: "─", v: "│",
    ml: "├", mr: "┤",
    cross: "┼",
};
const toolBox = {
    tl: "┌", tr: "┐",
    bl: "└", br: "┘",
    h: "─", v: "│",
    ml: "├", mr: "┤",
};
function termWidth() {
    return process.stdout.columns || 80;
}
function pad(str, len, char = " ") {
    while (str.length < len)
        str += char;
    return str;
}
function truncate(str, maxLen) {
    if (str.length <= maxLen)
        return str;
    return str.slice(0, maxLen - 3) + "...";
}
function stripAnsi(str) {
    return str.replace(/\x1b\[[0-9;]*m/g, "");
}
function visibleLength(str) {
    return stripAnsi(str);
}
function hRule(label = "", color = c.gray, width = termWidth()) {
    if (!label) {
        return color + box.h.repeat(width) + c.reset;
    }
    const labelStr = ` ${label} `;
    const side = Math.floor((width - labelStr.length) / 2);
    const left = box.h.repeat(Math.max(0, side));
    const right = box.h.repeat(Math.max(0, width - side - labelStr.length));
    return color + left + c.reset + c.bold + color + labelStr + c.reset + color + right + c.reset;
}
function drawBox(lines, headerLabel, headerColor, width) {
    const innerWidth = width - 2;
    const headerText = ` ${headerLabel} `;
    const headerPad = Math.max(0, innerWidth - stripAnsi(headerText).length);
    const topLine = headerColor + toolBox.tl + toolBox.h +
        c.bold + headerColor + headerText + c.reset +
        headerColor + toolBox.h.repeat(headerPad) +
        toolBox.tr + c.reset;
    const contentLines = lines.map((line) => {
        const visible = stripAnsi(line);
        const padLen = Math.max(0, innerWidth - visible.length);
        return headerColor + toolBox.v + c.reset + " " + line + " ".repeat(Math.max(0, padLen - 1)) + headerColor + toolBox.v + c.reset;
    });
    const bottomLine = headerColor + toolBox.bl + toolBox.h.repeat(innerWidth) + toolBox.br + c.reset;
    return [topLine, ...contentLines, bottomLine].join("\n");
}
function formatResult(result, maxLines = 20) {
    let str;
    try {
        str = typeof result === "string" ? result : JSON.stringify(result, null, 2);
    }
    catch {
        str = String(result);
    }
    const lines = str.split("\n");
    if (lines.length <= maxLines)
        return lines;
    const head = lines.slice(0, Math.floor(maxLines * 0.7));
    const tail = lines.slice(lines.length - Math.floor(maxLines * 0.2));
    const omitted = lines.length - head.length - tail.length;
    return [
        ...head,
        c.dim + `  ... (${omitted} lines omitted) ...` + c.reset,
        ...tail,
    ];
}
function colorizeJson(lines) {
    return lines.map((line) => {
        if (line.includes("\x1b["))
            return line;
        return line
            .replace(/"([^"]+)":/g, `${c.cyan}"$1"${c.reset}:`)
            .replace(/: "([^"]*)"/g, `: ${c.brightGreen}"$1"${c.reset}`)
            .replace(/: (-?\d+\.?\d*)/g, `: ${c.brightYellow}$1${c.reset}`)
            .replace(/: (true|false)/g, `: ${c.brightMagenta}$1${c.reset}`)
            .replace(/: null/g, `: ${c.dim}null${c.reset}`);
    });
}
const agentColors = [c.brightCyan, c.brightMagenta, c.brightGreen, c.brightYellow, c.brightBlue];
const agentColorMap = new Map();
let agentColorIdx = 0;
function agentColor(name) {
    if (!agentColorMap.has(name)) {
        agentColorMap.set(name, agentColors[agentColorIdx++ % agentColors.length]);
    }
    return agentColorMap.get(name);
}
const toolTimers = new Map();
class FancyRenderer {
    activeTaskId;
    emitter = new events_1.EventEmitter();
    paused = false;
    bufferedEvents = [];
    setActiveTaskId(taskId) {
        this.activeTaskId = taskId;
    }
    getActiveTaskId() {
        return this.activeTaskId;
    }
    isActiveTask(taskId) {
        if (!this.activeTaskId)
            return true;
        return taskId === this.activeTaskId;
    }
    onLog(handler) {
        this.emitter.on("log", handler);
    }
    onAgentStatus(handler) {
        this.emitter.on("agentStatus", handler);
    }
    onToolCall(handler) {
        this.emitter.on("toolCall", handler);
    }
    onToolResult(handler) {
        this.emitter.on("toolResult", handler);
    }
    onAgentMessage(handler) {
        this.emitter.on("agentMessage", handler);
    }
    onAgentDone(handler) {
        this.emitter.on("agentDone", handler);
    }
    pause() {
        this.paused = true;
    }
    resume() {
        this.paused = false;
        const buffered = this.bufferedEvents.splice(0);
        for (const event of buffered) {
            this.render(event);
        }
    }
    render(event) {
        if (!this.isActiveTask(event.taskId))
            return;
        if (this.paused) {
            this.bufferedEvents.push(event);
            return;
        }
        switch (event.type) {
            case "log":
                this.emitter.emit("log", event);
                this.renderLog(event);
                break;
            case "agentStatus":
                this.emitter.emit("agentStatus", event);
                this.renderAgentStatus(event);
                break;
            case "toolCall":
                this.emitter.emit("toolCall", event);
                this.renderToolCall(event);
                break;
            case "toolResult":
                this.emitter.emit("toolResult", event);
                this.renderToolResult(event);
                break;
            case "agentMessage":
                this.emitter.emit("agentMessage", event);
                this.renderAgentMessage(event);
                break;
            case "agentDone":
                this.emitter.emit("agentDone", event);
                this.renderAgentDone(event);
                break;
        }
    }
    renderLog(event) {
        const width = termWidth();
        const levelColors = {
            info: c.brightBlue,
            warn: c.brightYellow,
            error: c.red,
        };
        const icons = {
            info: "ℹ",
            warn: "⚠",
            error: "✖",
        };
        const col = levelColors[event.level] || c.gray;
        const icon = icons[event.level] || "·";
        const prefix = col + icon + c.reset + " " + c.dim + `[${event.agentName}]` + c.reset + " ";
        const msg = event.message;
        console.log(prefix + msg);
    }
    renderAgentStatus(event) {
        const agentCol = agentColor(event.agentName);
        const cost = c.brightGreen + `$${event.details.totalCostUsd.toFixed(3)}` + c.reset;
        const elapsed = c.brightYellow + `${Math.floor(event.details.elapsedMs / 1000)}s` + c.reset;
        let extras = "";
        if (event.details.remainingTimeMs !== undefined) {
            extras += c.dim + ` ~${Math.floor(event.details.remainingTimeMs / 1000)}s left` + c.reset;
        }
        if (event.details.remainingTurns !== undefined) {
            extras += c.dim + ` ${event.details.remainingTurns} turns` + c.reset;
        }
        const badge = `${c.dim}●${c.reset} ${agentCol}${event.agentName}${c.reset}`;
        console.log(`\n${badge} ${cost} ${elapsed}${extras}`);
    }
    renderToolCall(event) {
        const width = termWidth();
        const toolName = event.toolCall.function.name;
        const timerKey = `${event.taskId}:${toolName}:${Date.now()}`;
        toolTimers.set(`${event.taskId}:${toolName}`, Date.now());
        let args;
        try {
            args = JSON.parse(event.toolCall.function.arguments || "{}");
        }
        catch {
            args = event.toolCall.function.arguments;
        }
        const agentCol = agentColor(event.agentName);
        const headerLabel = `⚡ ${c.bold}${toolName}${c.reset}${c.brightYellow}`;
        const argsLines = colorizeJson(formatResult(args, 10));
        const boxStr = drawBox(argsLines, `⚡ ${toolName}`, c.brightYellow, Math.min(width, 100));
        console.log("");
        console.log(c.dim + agentCol + `[${event.agentName}]` + c.reset +
            c.dim + " called tool:" + c.reset);
        console.log(boxStr);
    }
    renderToolResult(event) {
        const width = termWidth();
        const toolName = event.toolCall.function.name;
        const startTime = toolTimers.get(`${event.taskId}:${toolName}`);
        const elapsed = startTime ? ((Date.now() - startTime) / 1000).toFixed(3) + "s" : "";
        toolTimers.delete(`${event.taskId}:${toolName}`);
        const resultLines = colorizeJson(formatResult(event.result, 25));
        const elapsedStr = elapsed ? c.dim + ` ⏱ ${elapsed}` + c.reset : "";
        const headerLabel = `✓ ${toolName}${elapsedStr}`;
        const boxStr = drawBox(resultLines, `✓ ${toolName} ${elapsed}`, c.brightGreen, Math.min(width, 100));
        console.log(boxStr);
    }
    renderAgentMessage(event) {
        const width = termWidth();
        const agentCol = agentColor(event.agentName);
        if (event.role === "assistant") {
            const badge = ` ${agentCol}${c.bold} ${event.agentName} ${c.reset} `;
            const badgeLen = stripAnsi(badge).length;
            const lineLen = Math.min(width, 80);
            const leftPad = 2;
            const rightPad = Math.max(0, lineLen - leftPad - badgeLen);
            const separator = c.gray + box.tl + box.h.repeat(leftPad) + c.reset +
                badge +
                c.gray + box.h.repeat(rightPad) + box.tr + c.reset;
            console.log("\n" + separator);
            const wrapWidth = Math.min(width - 4, 76);
            const words = event.message.split(" ");
            const wrappedLines = [];
            let currentLine = "";
            for (const word of words) {
                if (currentLine.length + word.length + 1 > wrapWidth) {
                    if (currentLine)
                        wrappedLines.push(currentLine);
                    currentLine = word;
                }
                else {
                    currentLine = currentLine ? currentLine + " " + word : word;
                }
            }
            if (currentLine)
                wrappedLines.push(currentLine);
            for (const line of wrappedLines) {
                console.log(c.gray + box.v + c.reset + "  " + c.brightWhite + line + c.reset);
            }
            const bottomSep = c.gray + box.bl + box.h.repeat(lineLen - 2) + box.br + c.reset;
            console.log(bottomSep);
        }
        else {
            console.log(c.dim + "  › " + c.reset + event.message);
        }
    }
    renderAgentDone(event) {
        const width = termWidth();
        const agentCol = agentColor(event.agentName);
        const costStr = event.totalCost
            ? c.brightGreen + `$${event.totalCost.toFixed(4)}` + c.reset
            : "";
        const label = ` ${agentCol}${c.bold}✔ ${event.agentName} done${c.reset}${costStr ? "  " + costStr : ""} `;
        console.log("\n" + hRule(stripAnsi(label), agentCol, Math.min(width, 80)));
        if (event.totalCost) {
            console.log(c.dim + "  Total cost: " + c.reset +
                c.brightGreen + `$${event.totalCost.toFixed(4)}` + c.reset);
        }
        console.log("");
    }
    logMessages(events, count = 10) {
        const recent = events.slice(-count);
        process.stdout.write(`\n[logs] Last ${recent.length} messages:\n`);
        process.stdout.write("-".repeat(60) + "\n");
        for (const event of recent) {
            this.render(event);
        }
        process.stdout.write("-".repeat(60) + "\n");
    }
}
exports.FancyRenderer = FancyRenderer;
exports.default = FancyRenderer;
//# sourceMappingURL=FancyRenderer.js.map