import { TuiClient, readTuiPort } from "./client.js";
import { TuiRenderer, splitGraphemes } from "./renderer.js";
const HELP_TEXT = `Available commands:
  /status          Show agent status
  /logs [n]        Show recent log entries (default: 20)
  /restart         Restart agent process
  /upgrade [tag]   Upgrade agent (default: latest)
  /rdp [minutes]   Generate remote desktop link
  /sessions        Show session info
  /switch <target> Switch active session (general|coding)
  /stop            Request agent to stop current task
  /wake            Send wake signal to agent
  /help            Show this help
  /quit            Exit TUI

Any other text is sent as natural language to the agent.`;
function formatUptime(seconds) {
    const h = Math.floor(seconds / 3600);
    const m = Math.floor((seconds % 3600) / 60);
    const s = seconds % 60;
    if (h > 0)
        return `${h}h ${m}m ${s}s`;
    if (m > 0)
        return `${m}m ${s}s`;
    return `${s}s`;
}
function formatSessionInfo(status) {
    const lines = [
        `Active session: ${status.activeSession}`,
        `Coding session:`,
        `  Status: ${status.codingSession.status}`,
    ];
    if (status.codingSession.taskSummary) {
        lines.push(`  Task: ${status.codingSession.taskSummary}`);
    }
    if (status.codingSession.cwd) {
        lines.push(`  CWD: ${status.codingSession.cwd}`);
    }
    return lines.join("\n");
}
function formatStatus(status) {
    return [
        `Agent: ${status.agentName} v${status.version}`,
        `Profile: ${status.profile}`,
        `PID: ${status.pid}`,
        `State: ${status.busy ? "busy" : "idle"}${status.compacting ? " (compacting)" : ""}${status.stopRequested ? " (stop requested)" : ""}`,
        `Active session: ${status.activeSession}`,
        `Queue: ${status.queueSize} message(s)`,
        `Uptime: ${formatUptime(status.uptime)}`,
        `Channels: ${status.channels.join(", ")}`,
    ].join("\n");
}
export async function runTui(_args) {
    const port = readTuiPort();
    if (!port) {
        console.error("Cannot find TUI server. Is the daemon running?");
        console.error("Start it with: visionclaw daemon start");
        process.exit(1);
    }
    const client = new TuiClient(port);
    const renderer = new TuiRenderer();
    let currentStatus = null;
    try {
        currentStatus = await client.getStatus();
        renderer.setStatus(currentStatus);
        renderer.setConnected(true);
    }
    catch {
        console.error(`Cannot connect to agent on port ${port}. Is the daemon running?`);
        console.error(`Start it with: visionclaw daemon start`);
        process.exit(1);
    }
    if (!process.stdin.isTTY) {
        console.error("TUI requires an interactive terminal.");
        process.exit(1);
    }
    process.stdin.setRawMode(true);
    process.stdin.resume();
    process.stdin.setEncoding("utf-8");
    process.stdout.write("\x1b[?1049h");
    process.stdout.write("\x1b[2J");
    let inputBuffer = "";
    let cursorPos = 0; // Grapheme index, not UTF-16 offset.
    const inputHistory = [];
    let historyIndex = -1;
    function addEntry(entry) {
        renderer.addStreamEntry(entry);
        renderer.render();
    }
    function now() {
        return new Date().toISOString();
    }
    const eventStream = client.connectEvents((event) => {
        if (event.type === "reply" && event.message) {
            addEntry({ timestamp: event.timestamp, type: "reply", text: event.message });
        }
        else if (event.type === "ack" && event.commandId) {
            addEntry({ timestamp: event.timestamp, type: "ack", text: `Command ${event.commandId.slice(0, 12)}…`, state: event.commandState });
        }
        else if (event.type === "error" && event.message) {
            addEntry({ timestamp: event.timestamp, type: "error", text: event.message });
        }
    }, (_err) => {
        renderer.setConnected(false);
        renderer.render();
    });
    const statusInterval = setInterval(() => {
        void (async () => {
            try {
                currentStatus = await client.getStatus();
                renderer.setStatus(currentStatus);
                renderer.setConnected(true);
                renderer.render();
            }
            catch {
                renderer.setConnected(false);
                renderer.render();
            }
        })();
    }, 3000);
    addEntry({ timestamp: now(), type: "info", text: `Connected to ${currentStatus.agentName}. Type /help for commands.` });
    process.on("SIGWINCH", () => {
        renderer.updateSize();
        renderer.render();
    });
    function cleanup() {
        clearInterval(statusInterval);
        eventStream.close();
        process.stdout.write("\x1b[?1049l");
        process.stdout.write("\x1b[?25h");
        if (process.stdin.isTTY)
            process.stdin.setRawMode(false);
    }
    async function handleSlashCommand(cmd) {
        const parts = cmd.trim().split(/\s+/);
        const command = parts[0].toLowerCase();
        const args = parts.slice(1);
        switch (command) {
            case "/help":
                addEntry({ timestamp: now(), type: "command", text: HELP_TEXT });
                break;
            case "/quit":
            case "/exit":
                cleanup();
                process.exit(0);
                break;
            case "/status":
                try {
                    const status = await client.getStatus();
                    renderer.setStatus(status);
                    addEntry({ timestamp: now(), type: "command", text: formatStatus(status) });
                }
                catch (err) {
                    addEntry({ timestamp: now(), type: "error", text: `Failed to get status: ${err instanceof Error ? err.message : String(err)}` });
                }
                break;
            case "/sessions":
                if (currentStatus) {
                    addEntry({ timestamp: now(), type: "command", text: formatSessionInfo(currentStatus) });
                }
                else {
                    addEntry({ timestamp: now(), type: "error", text: "No status available" });
                }
                break;
            case "/logs": {
                const count = args[0] ? parseInt(args[0], 10) : 20;
                try {
                    const logs = await client.getLogs(count);
                    if (logs.length === 0) {
                        addEntry({ timestamp: now(), type: "info", text: "No recent logs." });
                    }
                    else {
                        for (const log of logs) {
                            addEntry({
                                timestamp: log.timestamp,
                                type: log.level === "error" ? "error" : "info",
                                text: `[${log.category}] ${log.message}`,
                            });
                        }
                    }
                }
                catch (err) {
                    addEntry({ timestamp: now(), type: "error", text: `Failed to get logs: ${err instanceof Error ? err.message : String(err)}` });
                }
                break;
            }
            case "/stop":
                try {
                    await client.sendMessage("/stop");
                    addEntry({ timestamp: now(), type: "command", text: "Stop requested." });
                }
                catch (err) {
                    addEntry({ timestamp: now(), type: "error", text: `Failed to request stop: ${err instanceof Error ? err.message : String(err)}` });
                }
                break;
            case "/restart":
                try {
                    const result = await client.restart();
                    addEntry({ timestamp: now(), type: "command", text: result.message });
                }
                catch (err) {
                    addEntry({ timestamp: now(), type: "error", text: `Failed: ${err instanceof Error ? err.message : String(err)}` });
                }
                break;
            case "/upgrade": {
                const tag = args[0] ?? "latest";
                addEntry({ timestamp: now(), type: "info", text: `Upgrading to ${tag}… (this may take a moment)` });
                try {
                    const result = await client.upgrade(tag);
                    addEntry({ timestamp: now(), type: "command", text: result.message });
                }
                catch (err) {
                    addEntry({ timestamp: now(), type: "error", text: `Failed: ${err instanceof Error ? err.message : String(err)}` });
                }
                break;
            }
            case "/rdp": {
                const ttl = args[0] ? parseInt(args[0], 10) : 30;
                try {
                    const result = await client.rdp(ttl);
                    if (result.url) {
                        addEntry({ timestamp: now(), type: "command", text: `Remote desktop: ${result.url}` });
                    }
                    else {
                        addEntry({ timestamp: now(), type: "error", text: result.error ?? "Failed to generate RDP link" });
                    }
                }
                catch (err) {
                    addEntry({ timestamp: now(), type: "error", text: `Failed: ${err instanceof Error ? err.message : String(err)}` });
                }
                break;
            }
            case "/switch": {
                const target = args[0]?.toLowerCase();
                if (target !== "general" && target !== "coding") {
                    addEntry({ timestamp: now(), type: "error", text: "Usage: /switch general|coding" });
                }
                else {
                    try {
                        const result = await client.switchSession(target);
                        addEntry({ timestamp: now(), type: "command", text: `Switched to ${result.activeSession} session.` });
                    }
                    catch (err) {
                        addEntry({ timestamp: now(), type: "error", text: `Failed: ${err instanceof Error ? err.message : String(err)}` });
                    }
                }
                break;
            }
            case "/wake":
                try {
                    const result = await client.wake();
                    addEntry({ timestamp: now(), type: "command", text: result.message });
                }
                catch (err) {
                    addEntry({ timestamp: now(), type: "error", text: `Failed: ${err instanceof Error ? err.message : String(err)}` });
                }
                break;
            default:
                addEntry({ timestamp: now(), type: "error", text: `Unknown command: ${command}. Type /help for available commands.` });
        }
    }
    async function submitInput() {
        const text = inputBuffer.trim();
        if (!text)
            return;
        inputHistory.push(text);
        historyIndex = -1;
        inputBuffer = "";
        cursorPos = 0;
        renderer.setInput(inputBuffer, cursorPos);
        addEntry({ timestamp: now(), type: "input", text });
        if (text.startsWith("/")) {
            await handleSlashCommand(text);
        }
        else {
            try {
                const result = await client.sendMessage(text);
                if (!result.ok) {
                    addEntry({ timestamp: now(), type: "error", text: result.error ?? "Failed to send message" });
                }
                else {
                    addEntry({
                        timestamp: now(),
                        type: "ack",
                        text: `Queued ${result.messageId?.slice(0, 12) ?? "message"} — waiting for the agent loop to pick it up.`,
                        state: "queued",
                    });
                }
            }
            catch (err) {
                addEntry({ timestamp: now(), type: "error", text: `Failed to send: ${err instanceof Error ? err.message : String(err)}` });
            }
        }
    }
    function inputParts() {
        return splitGraphemes(inputBuffer);
    }
    function setInputParts(parts, nextCursor) {
        inputBuffer = parts.join("");
        cursorPos = Math.max(0, Math.min(nextCursor, parts.length));
        renderer.setInput(inputBuffer, cursorPos);
        renderer.render();
    }
    function insertText(text) {
        const parts = inputParts();
        const inserted = splitGraphemes(text).filter((part) => !/^\p{Cc}+$/u.test(part));
        if (inserted.length === 0)
            return;
        parts.splice(cursorPos, 0, ...inserted);
        setInputParts(parts, cursorPos + inserted.length);
    }
    process.stdin.on("data", (data) => {
        for (let i = 0; i < data.length; i++) {
            const ch = data[i] ?? "";
            const code = ch.charCodeAt(0);
            if (code === 3 || code === 4) {
                cleanup();
                process.exit(0);
            }
            if (code === 13) {
                void submitInput();
                continue;
            }
            if (code === 127 || code === 8) {
                if (cursorPos > 0) {
                    const parts = inputParts();
                    parts.splice(cursorPos - 1, 1);
                    setInputParts(parts, cursorPos - 1);
                }
                continue;
            }
            if (ch === "\x1b" && i + 2 < data.length && data[i + 1] === "[") {
                const seq = data[i + 2];
                i += 2;
                if (seq === "D") {
                    if (cursorPos > 0)
                        cursorPos--;
                }
                else if (seq === "C") {
                    if (cursorPos < inputParts().length)
                        cursorPos++;
                }
                else if (seq === "A") {
                    if (inputHistory.length > 0) {
                        if (historyIndex < 0)
                            historyIndex = inputHistory.length;
                        if (historyIndex > 0) {
                            historyIndex--;
                            inputBuffer = inputHistory[historyIndex];
                            cursorPos = inputParts().length;
                        }
                    }
                }
                else if (seq === "B") {
                    if (historyIndex >= 0) {
                        historyIndex++;
                        if (historyIndex >= inputHistory.length) {
                            historyIndex = -1;
                            inputBuffer = "";
                        }
                        else {
                            inputBuffer = inputHistory[historyIndex];
                        }
                        cursorPos = inputParts().length;
                    }
                }
                else if (seq === "H") {
                    cursorPos = 0;
                }
                else if (seq === "F") {
                    cursorPos = inputParts().length;
                }
                renderer.setInput(inputBuffer, cursorPos);
                renderer.render();
                continue;
            }
            if (code === 1) {
                cursorPos = 0;
                renderer.setInput(inputBuffer, cursorPos);
                renderer.render();
                continue;
            }
            if (code === 5) {
                cursorPos = inputParts().length;
                renderer.setInput(inputBuffer, cursorPos);
                renderer.render();
                continue;
            }
            if (code === 21) {
                inputBuffer = "";
                cursorPos = 0;
                renderer.setInput(inputBuffer, cursorPos);
                renderer.render();
                continue;
            }
            if (code === 11) {
                setInputParts(inputParts().slice(0, cursorPos), cursorPos);
                continue;
            }
            if (code >= 32) {
                let text = ch;
                while (i + 1 < data.length) {
                    const next = data[i + 1] ?? "";
                    const nextCode = next.charCodeAt(0);
                    if (nextCode < 32 || nextCode === 127 || next === "\x1b")
                        break;
                    text += next;
                    i++;
                }
                insertText(text);
            }
        }
    });
}
//# sourceMappingURL=index.js.map