export class RemoteTerminalSurface {
    id;
    ws;
    nodeId;
    type = "terminal";
    _state = "idle";
    _sessionId = null;
    _startedAt = null;
    _cwd = null;
    _pid = null;
    _outputBuffer = [];
    _outputListeners = [];
    _cols;
    _rows;
    _shell;
    _lastActivityAt = Date.now();
    onOutput;
    onStateChange;
    constructor(id, ws, nodeId, opts = {}) {
        this.id = id;
        this.ws = ws;
        this.nodeId = nodeId;
        this._cols = opts.cols ?? 120;
        this._rows = opts.rows ?? 30;
        this._shell = opts.shell ?? null;
    }
    get state() {
        return this._state;
    }
    get sessionId() {
        return this._sessionId;
    }
    async start(input) {
        if (this._state === "running")
            return;
        this._setState("starting");
        this._sessionId = input.sessionId;
        this._cwd = input.projectRoot;
        this._startedAt = new Date().toISOString();
        try {
            const result = await this.ws.proxyTerminalOp(this.nodeId, "start", {
                terminalId: this.id,
                sessionId: input.sessionId,
                projectRoot: input.projectRoot,
                cols: this._cols,
                rows: this._rows,
                ...(this._shell ? { shell: this._shell } : {}),
            }, 15_000);
            this._pid = typeof result?.pid === "number" ? result.pid : null;
            this._shell = typeof result?.shell === "string" && result.shell ? result.shell : this._shell;
            this._setState("running");
        }
        catch (err) {
            this._setState("error");
            throw err;
        }
    }
    async stop(_input) {
        if (this._state === "stopped")
            return;
        this._setState("stopping");
        try {
            await this.ws.proxyTerminalOp(this.nodeId, "stop", { terminalId: this.id }, 5_000);
        }
        catch {
            // The owning node may already be gone. The gateway surface should still close.
        }
        this._pid = null;
        this._setState("stopped");
    }
    write(data) {
        if (this._state !== "running")
            return;
        this._lastActivityAt = Date.now();
        try {
            this.ws.sendTerminalOp(this.nodeId, "input", { terminalId: this.id, data });
        }
        catch (err) {
            this.ingestOutput(`\r\nRemote terminal input failed: ${err instanceof Error ? err.message : String(err)}\r\n`);
        }
    }
    resize(cols, rows) {
        this._cols = cols;
        this._rows = rows;
        if (this._state !== "running")
            return;
        try {
            this.ws.sendTerminalOp(this.nodeId, "resize", { terminalId: this.id, cols, rows });
        }
        catch {
            // Resize is best-effort; keep local dimensions for subsequent snapshots.
        }
    }
    waitForPrompt(timeoutMs = 5000) {
        return new Promise((resolve) => setTimeout(resolve, Math.min(timeoutMs, 25)));
    }
    addOutputListener(listener) {
        this._outputListeners.push(listener);
    }
    removeOutputListener(listener) {
        const idx = this._outputListeners.indexOf(listener);
        if (idx !== -1)
            this._outputListeners.splice(idx, 1);
    }
    ingestOutput(data) {
        if (!data)
            return;
        this._lastActivityAt = Date.now();
        this._outputBuffer.push(data);
        if (this._outputBuffer.length > 10000) {
            this._outputBuffer = this._outputBuffer.slice(-5000);
        }
        this.onOutput?.(data);
        for (const listener of this._outputListeners) {
            listener(data);
        }
    }
    ingestExit(exitCode, signal) {
        this._pid = null;
        if (this._state !== "stopped") {
            this.ingestOutput(`\r\n[remote terminal exited${typeof exitCode === "number" ? ` with code ${exitCode}` : ""}${signal ? `, signal ${signal}` : ""}]\r\n`);
        }
        this._setState("stopped");
    }
    snapshot() {
        return {
            id: this.id,
            type: this.type,
            state: this._state,
            sessionId: this._sessionId ?? "",
            startedAt: this._startedAt ?? undefined,
            metadata: {
                shell: this._shell ?? null,
                cols: this._cols,
                rows: this._rows,
                pid: this._pid ?? null,
                cwd: this._cwd,
                nodeId: this.nodeId,
                remote: true,
            },
        };
    }
    getRecentOutput(lines = 100) {
        return this._outputBuffer.slice(-lines).join("");
    }
    touch() {
        this._lastActivityAt = Date.now();
    }
    get idleMs() {
        return Date.now() - this._lastActivityAt;
    }
    _setState(state) {
        this._state = state;
        this.onStateChange?.(state);
    }
}
//# sourceMappingURL=remote-terminal.js.map