import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import chalk from "chalk";
import { createReadStream, unwatchFile, watchFile } from "fs";
import { Box, render, Text, useApp, useInput, useStdout } from "ink";
import { useEffect, useRef, useState } from "react";
import { LOG_COLORS } from "./constants/log-colors.js";
// Compact ASCII logo for very small terminals
const COMPACT_LOGO = "d3k";
// Full ASCII logo lines as array for easier rendering
const FULL_LOGO = ["   ▐▌▄▄▄▄ █  ▄ ", "   ▐▌   █ █▄▀  ", "▗▞▀▜▌▀▀▀█ █ ▀▄ ", "▝▚▄▟▌▄▄▄█ █  █ "];
const TUIApp = ({ appPort, mcpPort, logFile, commandName, serversOnly, version, projectName, onShutdown, onStatusUpdate }) => {
    const [logs, setLogs] = useState([]);
    const [scrollOffset, setScrollOffset] = useState(0);
    const [initStatus, setInitStatus] = useState("Initializing...");
    const logIdCounter = useRef(0);
    const { exit } = useApp();
    const { stdout } = useStdout();
    const [terminalSize, setTerminalSize] = useState(() => ({
        width: stdout?.columns || 80,
        height: stdout?.rows || 24
    }));
    useEffect(() => {
        if (!stdout) {
            return;
        }
        const handleResize = () => {
            setTerminalSize({
                width: stdout.columns || 80,
                height: stdout.rows || 24
            });
        };
        stdout.on("resize", handleResize);
        return () => {
            if (typeof stdout.off === "function") {
                stdout.off("resize", handleResize);
            }
            else {
                stdout.removeListener("resize", handleResize);
            }
        };
    }, [stdout]);
    // Get terminal dimensions with fallbacks
    const termWidth = terminalSize.width;
    const termHeight = terminalSize.height;
    // Determine if we should use compact mode
    const isCompact = termWidth < 80 || termHeight < 20;
    const isVeryCompact = termWidth < 60 || termHeight < 15;
    // Provide status update function to parent
    useEffect(() => {
        onStatusUpdate(setInitStatus);
    }, [onStatusUpdate]);
    // Calculate available lines for logs dynamically based on terminal height and mode
    const calculateMaxVisibleLogs = () => {
        if (isVeryCompact) {
            // In very compact mode, use most of the screen for logs
            return Math.max(3, termHeight - 8);
        }
        else if (isCompact) {
            // In compact mode, reduce header size
            return Math.max(3, termHeight - 10);
        }
        else {
            // Normal mode calculation
            const headerLines = 12;
            const logBoxHeaderLines = 3;
            const logBoxFooterLines = scrollOffset > 0 ? 3 : 1;
            const safetyBuffer = 1;
            const totalReservedLines = headerLines + logBoxHeaderLines + logBoxFooterLines + safetyBuffer;
            return Math.max(3, termHeight - totalReservedLines);
        }
    };
    const maxVisibleLogs = calculateMaxVisibleLogs();
    useEffect(() => {
        let logStream;
        let buffer = "";
        const appendLog = (line) => {
            const newLog = {
                id: logIdCounter.current++,
                content: line
            };
            setLogs((prevLogs) => {
                const updated = [...prevLogs, newLog];
                // Keep only last 1000 logs to prevent memory issues
                if (updated.length > 1000) {
                    return updated.slice(-1000);
                }
                return updated;
            });
            // Auto-scroll to bottom
            setScrollOffset(0);
        };
        // Create a read stream for the log file
        logStream = createReadStream(logFile, {
            encoding: "utf8",
            start: 0
        });
        logStream.on("data", (chunk) => {
            buffer += chunk.toString();
            const lines = buffer.split("\n");
            // Keep the last incomplete line in buffer
            buffer = lines.pop() || "";
            // Add complete lines to logs
            for (const line of lines) {
                if (line.trim()) {
                    appendLog(line);
                }
            }
        });
        logStream.on("error", (error) => {
            appendLog(chalk.red(`Error reading log file: ${error.message}`));
        });
        // Watch for new content
        watchFile(logFile, { interval: 100 }, (curr, prev) => {
            if (curr.size > prev.size) {
                // File has grown, read new content
                const stream = createReadStream(logFile, {
                    encoding: "utf8",
                    start: prev.size
                });
                let watchBuffer = "";
                stream.on("data", (chunk) => {
                    watchBuffer += chunk.toString();
                    const lines = watchBuffer.split("\n");
                    watchBuffer = lines.pop() || "";
                    for (const line of lines) {
                        if (line.trim()) {
                            appendLog(line);
                        }
                    }
                });
            }
        });
        // Cleanup
        return () => {
            if (logStream) {
                logStream.destroy();
            }
            unwatchFile(logFile);
        };
    }, [logFile]);
    // Handle keyboard input
    useInput((input, key) => {
        if (input === "q" || (key.ctrl && input === "c")) {
            onShutdown();
            exit();
        }
        else if (key.upArrow) {
            setScrollOffset((prev) => Math.min(prev + 1, Math.max(0, logs.length - maxVisibleLogs)));
        }
        else if (key.downArrow) {
            setScrollOffset((prev) => Math.max(0, prev - 1));
        }
        else if (key.pageUp) {
            setScrollOffset((prev) => Math.min(prev + maxVisibleLogs, Math.max(0, logs.length - maxVisibleLogs)));
        }
        else if (key.pageDown) {
            setScrollOffset((prev) => Math.max(0, prev - maxVisibleLogs));
        }
        else if (input === "g" && key.shift) {
            // Shift+G to go to end
            setScrollOffset(0);
        }
        else if (input === "g" && !key.shift) {
            // g to go to beginning
            setScrollOffset(Math.max(0, logs.length - maxVisibleLogs));
        }
    });
    // Calculate visible logs
    const visibleLogs = logs.slice(Math.max(0, logs.length - maxVisibleLogs - scrollOffset), logs.length - scrollOffset);
    // Render compact header for small terminals
    const renderCompactHeader = () => (_jsx(Box, { borderStyle: "single", borderColor: "#A18CE5", paddingX: 1, marginBottom: 1, children: _jsxs(Box, { flexDirection: "column", width: "100%", children: [_jsxs(Box, { children: [_jsx(Text, { color: "#A18CE5", bold: true, children: COMPACT_LOGO }), _jsxs(Text, { children: [" v", version, " "] }), initStatus && _jsxs(Text, { dimColor: true, children: ["- ", initStatus] })] }), !isVeryCompact && (_jsxs(_Fragment, { children: [_jsxs(Text, { dimColor: true, children: ["App: localhost:", appPort, " | MCP: localhost:", mcpPort] }), _jsx(Text, { dimColor: true, children: "\u2191/\u2193 scroll | Ctrl-C quit" })] }))] }) }));
    // Render normal header
    const renderNormalHeader = () => (_jsxs(Box, { borderStyle: "round", borderColor: "#A18CE5", paddingX: 2, paddingY: 1, marginBottom: 1, flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", gap: 3, children: [_jsx(Box, { flexDirection: "column", alignItems: "flex-start", children: FULL_LOGO.map((line) => (_jsx(Text, { color: "#A18CE5", bold: true, children: line }, line))) }), _jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Text, { color: "#A18CE5", bold: true, children: [commandName, " v", version, " ", initStatus ? `- ${initStatus}` : "is running!"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: "cyan", children: ["\uD83C\uDF10 Your App: http://localhost:", appPort] }), _jsxs(Text, { color: "cyan", children: ["\uD83E\uDD16 MCP Server: http://localhost:", mcpPort, "/mcp"] }), _jsxs(Text, { color: "cyan", children: ["\uD83D\uDCF8 Visual Timeline: http://localhost:", mcpPort, "/logs", projectName ? `?project=${encodeURIComponent(projectName)}` : ""] }), serversOnly && _jsx(Text, { color: "cyan", children: "\uD83D\uDDA5\uFE0F Servers-only mode - use Chrome extension for browser monitoring" })] })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\uD83D\uDCA1 Controls: \u2191/\u2193 scroll | PgUp/PgDn page | g/G start/end | Ctrl-C quit" }) })] }));
    return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [isCompact ? renderCompactHeader() : renderNormalHeader(), _jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, flexGrow: 1, minHeight: 0, children: [!isVeryCompact && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "gray", dimColor: true, children: ["Logs (", logs.length, " total", scrollOffset > 0 && `, scrolled up ${scrollOffset} lines`, ")"] }), _jsx(Text, { children: " " })] })), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: visibleLogs.length === 0 ? (_jsx(Text, { dimColor: true, children: "Waiting for logs..." })) : (visibleLogs.map((log) => {
                            // Parse log line to colorize different parts
                            const parts = log.content.match(/^\[(.*?)\] \[(.*?)\] (?:\[(.*?)\] )?(.*)$/);
                            if (parts) {
                                let [, timestamp, source, type, message] = parts;
                                // Replace specific emoji in common port-in-use error to avoid terminal width issues
                                if (message?.includes("ERROR: ⚠ Port") && message.includes("is in use by process")) {
                                    message = message.replace("ERROR: ⚠ Port", "ERROR: [!] Port");
                                }
                                // In very compact mode, simplify the output
                                if (isVeryCompact) {
                                    const shortSource = source === "BROWSER" ? "B" : "S";
                                    const shortType = type ? type.split(".")[0].charAt(0) : "";
                                    return (_jsxs(Text, { wrap: "truncate-end", children: [_jsxs(Text, { dimColor: true, children: ["[", shortSource, "]"] }), shortType && _jsxs(Text, { dimColor: true, children: ["[", shortType, "]"] }), _jsxs(Text, { children: [" ", message] })] }, log.id));
                                }
                                // Use shared color constants
                                const sourceColor = source === "BROWSER" ? LOG_COLORS.BROWSER : LOG_COLORS.SERVER;
                                const typeColors = {
                                    NETWORK: LOG_COLORS.NETWORK,
                                    "NETWORK.REQUEST": LOG_COLORS.NETWORK,
                                    "CONSOLE.ERROR": LOG_COLORS.CONSOLE_ERROR,
                                    "CONSOLE.WARN": LOG_COLORS.CONSOLE_WARN,
                                    "CONSOLE.INFO": LOG_COLORS.CONSOLE_INFO,
                                    "CONSOLE.LOG": LOG_COLORS.CONSOLE_LOG,
                                    "CONSOLE.DEBUG": LOG_COLORS.CONSOLE_DEBUG,
                                    "RUNTIME.ERROR": LOG_COLORS.ERROR,
                                    "CDP.ERROR": LOG_COLORS.CDP,
                                    "CHROME.ERROR": LOG_COLORS.ERROR,
                                    "CHROME.CRASH": LOG_COLORS.CRITICAL_ERROR,
                                    NAVIGATION: LOG_COLORS.PAGE,
                                    INTERACTION: LOG_COLORS.DOM,
                                    SCREENSHOT: LOG_COLORS.SCREENSHOT,
                                    PAGE: LOG_COLORS.PAGE,
                                    DOM: LOG_COLORS.DOM,
                                    CDP: LOG_COLORS.CDP,
                                    ERROR: LOG_COLORS.ERROR,
                                    "CRITICAL ERROR": LOG_COLORS.CRITICAL_ERROR
                                };
                                // In compact mode, skip padding
                                if (isCompact) {
                                    return (_jsxs(Text, { wrap: "truncate-end", children: [_jsxs(Text, { dimColor: true, children: ["[", timestamp, "]"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: sourceColor, bold: true, children: ["[", source.charAt(0), "]"] }), type && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsxs(Text, { color: typeColors[type] || "#A0A0A0", children: ["[", type, "]"] })] })), _jsxs(Text, { children: [" ", message] })] }, log.id));
                                }
                                // Normal mode with padding
                                // Calculate padding for source (BROWSER is 7, SERVER is 6)
                                const sourceSpacing = " ".repeat(Math.max(0, 7 - source.length));
                                // Calculate padding for type (NETWORK.REQUEST is longest at 15)
                                const typeSpacing = type ? " ".repeat(Math.max(0, 15 - type.length)) : "";
                                return (_jsxs(Text, { wrap: "truncate-end", children: [_jsxs(Text, { dimColor: true, children: ["[", timestamp, "]"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: sourceColor, bold: true, children: ["[", source, "]"] }), _jsxs(Text, { children: [sourceSpacing, " "] }), type && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: typeColors[type] || "#A0A0A0", children: ["[", type, "]"] }), _jsxs(Text, { children: [typeSpacing, " "] })] })), _jsx(Text, { children: message })] }, log.id));
                            }
                            // Fallback for unparsed lines
                            return (_jsx(Text, { wrap: "truncate-end", children: log.content }, log.id));
                        })) }), !isVeryCompact && logs.length > maxVisibleLogs && scrollOffset > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsxs(Text, { dimColor: true, children: ["(", scrollOffset, " lines below)"] })] }))] })] }));
};
export async function runTUI(options) {
    return new Promise((resolve) => {
        let statusUpdater = null;
        const app = render(_jsx(TUIApp, { ...options, onShutdown: () => {
                // Don't resolve here, just trigger shutdown
            }, onStatusUpdate: (fn) => {
                statusUpdater = fn;
            } }));
        // Give React time to set up the status updater
        setTimeout(() => {
            resolve({
                app,
                updateStatus: (status) => {
                    if (statusUpdater) {
                        statusUpdater(status);
                    }
                }
            });
        }, 100);
    });
}
//# sourceMappingURL=tui-interface-impl.js.map