"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TerminalManager = void 0;
const node_child_process_1 = require("node:child_process");
const node_os_1 = __importDefault(require("node:os"));
const node_pty_1 = require("node-pty");
const uuid_1 = require("uuid");
const terminal_buffer_1 = require("./terminal-buffer");
function getLoginShell() {
    if (process.platform === "win32") {
        return process.env.COMSPEC || "cmd.exe";
    }
    try {
        const entry = (0, node_child_process_1.execSync)(`getent passwd ${node_os_1.default.userInfo().username}`, { encoding: "utf8" }).trim();
        const shell = entry.split(":").pop();
        if (shell)
            return shell;
    }
    catch {
        // getent not available (macOS)
    }
    try {
        const shell = (0, node_child_process_1.execSync)(`dscl . -read /Users/${node_os_1.default.userInfo().username} UserShell`, { encoding: "utf8" });
        const match = shell.match(/UserShell:\s*(.+)/);
        if (match)
            return match[1].trim();
    }
    catch {
        // not macOS
    }
    return process.env.SHELL || "/bin/bash";
}
class TerminalManager {
    terminals = new Map();
    constructor() {
        setInterval(() => {
            for (const [id, term] of this.terminals) {
                try {
                    process.kill(term.pty.pid, 0);
                }
                catch {
                    this.terminals.delete(id);
                }
            }
        }, 15000);
    }
    createTerminal(cwd, shell, cols = 120, rows = 40) {
        const id = (0, uuid_1.v4)();
        const resolvedShell = shell || getLoginShell();
        const pty = (0, node_pty_1.spawn)(resolvedShell, ["-l"], {
            name: "xterm-256color",
            cols,
            rows,
            cwd,
            env: process.env,
        });
        const instance = { id, pty, cwd, cols, rows, buffer: "", detachOffset: 0, client: null };
        this.terminals.set(id, instance);
        pty.onData((data) => {
            (0, terminal_buffer_1.appendToBuffer)(instance, data);
            if (instance.client) {
                instance.client(data);
            }
        });
        pty.onExit(() => {
            instance.client = null;
            this.terminals.delete(id);
        });
        return id;
    }
    attachClient(id, sendFn) {
        const term = this.terminals.get(id);
        if (!term)
            return false;
        console.log(`[terminal] attachClient ${id.slice(0, 8)} buffer=${term.buffer.length}b detachOffset=${term.detachOffset} cols=${term.cols} rows=${term.rows}`);
        term.client = sendFn;
        return true;
    }
    getBuffer(id) {
        return this.terminals.get(id)?.buffer ?? "";
    }
    getDelta(id) {
        const term = this.terminals.get(id);
        if (!term)
            return "";
        if (term.detachOffset >= term.buffer.length)
            return "";
        return term.buffer.slice(term.detachOffset);
    }
    detachClient(id, sendFn) {
        const term = this.terminals.get(id);
        if (!term)
            return;
        if (sendFn && term.client !== sendFn)
            return; // a newer client already attached; ignore this stale close
        term.detachOffset = term.buffer.length;
        term.client = null;
    }
    getTerminal(id) {
        return this.terminals.get(id);
    }
    writeToTerminal(id, data) {
        const term = this.terminals.get(id);
        if (!term)
            return false;
        term.pty.write(data);
        return true;
    }
    resizeTerminal(id, cols, rows) {
        const term = this.terminals.get(id);
        if (!term)
            return false;
        if (term.cols === cols && term.rows === rows) {
            console.log(`[terminal] resize ${id.slice(0, 8)} no-op (already ${cols}x${rows})`);
            return true;
        }
        const colsChanged = term.cols !== cols;
        console.log(`[terminal] resize ${id.slice(0, 8)} ${term.cols}x${term.rows} -> ${cols}x${rows}${colsChanged ? " (cols changed, clearing buffer)" : ""}`);
        if (colsChanged) {
            term.buffer = "";
            term.detachOffset = 0;
        }
        term.pty.resize(cols, rows);
        term.cols = cols;
        term.rows = rows;
        return true;
    }
    destroyTerminal(id) {
        const term = this.terminals.get(id);
        if (!term)
            return false;
        term.client = null;
        try {
            term.pty.kill();
        }
        catch {
            // already dead
        }
        this.terminals.delete(id);
        return true;
    }
    listTerminals() {
        return Array.from(this.terminals.values()).map((t) => ({ id: t.id, cwd: t.cwd }));
    }
}
exports.TerminalManager = TerminalManager;
