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, 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: initialAppPort, mcpPort, logFile, commandName, serversOnly, version, projectName, onStatusUpdate, onAppPortUpdate }) => {
    const [logs, setLogs] = useState([]);
    const [scrollOffset, setScrollOffset] = useState(0);
    const [initStatus, setInitStatus] = useState("Initializing...");
    const [appPort, setAppPort] = useState(initialAppPort);
    const logIdCounter = useRef(0);
    const { stdout } = useStdout();
    const ctrlCMessageDefault = "^C quit";
    const [ctrlCMessage, setCtrlCMessage] = useState(ctrlCMessageDefault);
    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((status) => {
            // Check if this is the "Press Ctrl+C again" warning
            if (status?.includes("Press Ctrl+C again")) {
                // Update the bottom Ctrl+C message with warning emoji
                setCtrlCMessage("⚠️ ^C again to quit");
                // Clear the init status since we don't want it in the header anymore
                setInitStatus(null);
                // Reset after 3 seconds
                setTimeout(() => {
                    setCtrlCMessage(ctrlCMessageDefault);
                }, 3000);
            }
            else {
                setInitStatus(status);
            }
        });
    }, [onStatusUpdate]);
    // Provide app port update function to parent
    useEffect(() => {
        onAppPortUpdate((port) => {
            setAppPort(port);
        });
    }, [onAppPortUpdate]);
    // 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, account for bottom status line
            return Math.max(3, termHeight - 8);
        }
        else if (isCompact) {
            // In compact mode, reduce header size, account for bottom status line
            return Math.max(3, termHeight - 10);
        }
        else {
            // Normal mode calculation - account for all UI elements
            const headerBorderLines = 2; // Top border (with title) + bottom border
            const headerContentLines = 5; // Logo is 4 lines tall, +1 for padding
            const logBoxBorderLines = 2; // Top and bottom border of log box
            const logBoxHeaderLines = 2; // "Logs (X total)" text (no blank line after)
            const logBoxFooterLines = scrollOffset > 0 ? 2 : 0; // "(X lines below)" when scrolled
            const bottomStatusLine = 1; // Log path and quit message
            const safetyBuffer = 1; // Small buffer to prevent header from being pushed up
            const totalReservedLines = headerBorderLines +
                headerContentLines +
                logBoxBorderLines +
                logBoxHeaderLines +
                logBoxFooterLines +
                bottomStatusLine +
                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 (key.ctrl && input === "c") {
            // Send SIGINT to trigger main process shutdown handler
            process.kill(process.pid, "SIGINT");
        }
        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(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["App: localhost:", appPort, " | MCP: localhost:", mcpPort] }), _jsxs(Text, { dimColor: true, children: ["\uD83D\uDCF8 http://localhost:", mcpPort, "/logs", projectName ? `?project=${encodeURIComponent(projectName)}` : ""] })] }))] }) }));
    // Render normal header
    const renderNormalHeader = () => {
        // Create custom top border with title embedded (like Claude Code)
        const title = ` ${commandName} v${version} ${initStatus ? `- ${initStatus} ` : ""}`;
        const borderChar = "─";
        const leftPadding = 2;
        // Account for border characters and padding
        const availableWidth = termWidth - 2; // -2 for corner characters
        const titleLength = title.length;
        const rightBorderLength = Math.max(0, availableWidth - titleLength - leftPadding);
        const topBorderLine = `╭${borderChar.repeat(leftPadding)}${title}${borderChar.repeat(rightBorderLength)}╮`;
        return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "#A18CE5", children: topBorderLine }), _jsx(Box, { borderStyle: "round", borderColor: "#A18CE5", borderTop: false, paddingX: 1, paddingY: 1, children: _jsxs(Box, { flexDirection: "row", gap: 1, 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: "cyan", children: ["\uD83C\uDF10 App: http://localhost:", appPort] }), _jsxs(Text, { color: "cyan", children: ["\uD83E\uDD16 MCP: http://localhost:", mcpPort] }), _jsxs(Text, { color: "cyan", children: ["\uD83D\uDCF8 Logs: 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" }))] })] }) })] }));
    };
    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(Text, { color: "gray", dimColor: true, children: ["Logs (", logs.length, " total", scrollOffset > 0 && `, scrolled up ${scrollOffset} lines`, ")"] })), _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;
                                // Extract HTTP method from SERVER logs as a secondary tag
                                if (source === "SERVER" && !type && message) {
                                    const methodMatch = message.match(/^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s/);
                                    if (methodMatch) {
                                        type = methodMatch[1];
                                        message = message.slice(type.length + 1); // Remove method from message
                                    }
                                }
                                // Replace warning emoji in ERROR/WARNING messages for consistent terminal rendering
                                if (message && (type === "ERROR" || type === "WARNING")) {
                                    message = message.replace(/⚠/g, "[!]");
                                }
                                // 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,
                                    ERROR: LOG_COLORS.ERROR,
                                    WARNING: LOG_COLORS.WARNING,
                                    INFO: LOG_COLORS.INFO,
                                    LOG: LOG_COLORS.LOG,
                                    DEBUG: LOG_COLORS.DEBUG,
                                    SCREENSHOT: LOG_COLORS.SCREENSHOT,
                                    DOM: LOG_COLORS.DOM,
                                    CDP: LOG_COLORS.CDP,
                                    CHROME: LOG_COLORS.CHROME,
                                    CRASH: LOG_COLORS.CRASH,
                                    REPLAY: LOG_COLORS.REPLAY,
                                    NAVIGATION: LOG_COLORS.NAVIGATION,
                                    INTERACTION: LOG_COLORS.INTERACTION,
                                    GET: LOG_COLORS.SERVER,
                                    POST: LOG_COLORS.SERVER,
                                    PUT: LOG_COLORS.SERVER,
                                    DELETE: LOG_COLORS.SERVER,
                                    PATCH: LOG_COLORS.SERVER,
                                    HEAD: LOG_COLORS.SERVER,
                                    OPTIONS: LOG_COLORS.SERVER
                                };
                                // 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 minimal padding
                                // Single space after source
                                const sourceSpacing = "";
                                // Single space after type
                                const typeSpacing = "";
                                // For alignment: if no type tag, add spacing equivalent to a tag
                                // This aligns SERVER logs without tags with those that have tags
                                const alignmentSpacing = !type ? "       " : ""; // ~7 chars for average tag like [GET]
                                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: alignmentSpacing })), _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(Text, { dimColor: true, children: ["(", scrollOffset, " lines below)"] }))] }), _jsxs(Box, { paddingX: 1, justifyContent: "space-between", children: [_jsxs(Text, { color: "#A18CE5", children: ["\u23F5\u23F5", " ", isVeryCompact
                                ? logFile.split("/").slice(-2, -1)[0] || "logs" // Just show directory name
                                : logFile.replace(process.env.HOME || "", "~")] }), _jsx(Text, { color: "#A18CE5", children: ctrlCMessage })] })] }));
};
export async function runTUI(options) {
    return new Promise((resolve, reject) => {
        try {
            let statusUpdater = null;
            let appPortUpdater = null;
            const app = render(_jsx(TUIApp, { ...options, onStatusUpdate: (fn) => {
                    statusUpdater = fn;
                }, onAppPortUpdate: (fn) => {
                    appPortUpdater = fn;
                } }), { exitOnCtrlC: false });
            // Give React time to set up the updaters
            setTimeout(() => {
                resolve({
                    app,
                    updateStatus: (status) => {
                        if (statusUpdater) {
                            statusUpdater(status);
                        }
                    },
                    updateAppPort: (port) => {
                        if (appPortUpdater) {
                            appPortUpdater(port);
                        }
                    }
                });
            }, 100);
        }
        catch (error) {
            console.error("Error in runTUI render:", error);
            reject(error);
        }
    });
}
//# sourceMappingURL=tui-interface-impl.js.map