#!/usr/bin/env node
/**
 * PTY bridge — runs under Node.js to work around Bun's node-pty incompatibility.
 *
 * Protocol (line-delimited JSON over stdin/stdout):
 *   Daemon → Bridge (stdin):
 *     {"t":"i","d":"<base64>"}        — write input to PTY
 *     {"t":"s","c":<cols>,"r":<rows>} — resize PTY
 *     {"t":"c"}                       — close/kill PTY
 *
 *   Bridge → Daemon (stdout):
 *     {"t":"r","pid":<pid>}           — ready, PTY spawned
 *     {"t":"d","d":"<base64>"}        — PTY output (base64-encoded)
 *     {"t":"x","code":<n>,"signal":<n>} — PTY exited
 *     {"t":"e","msg":"<error>"}       — error
 */

import { createRequire } from "module";
import { createInterface } from "readline";
import { dirname, join } from "path";
import { fileURLToPath } from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const req = createRequire(import.meta.url);

function send(obj) {
  process.stdout.write(JSON.stringify(obj) + "\n");
}

// Parse spawn options from argv
// Usage: node pty-bridge.mjs <shell> <cols> <rows> [cwd] [envJSON]
const shell = process.argv[2] || "/bin/zsh";
const cols = parseInt(process.argv[3]) || 80;
const rows = parseInt(process.argv[4]) || 24;
const cwd = process.argv[5] || undefined;
const envJson = process.argv[6] || null;

let env = process.env;
if (envJson) {
  try {
    env = { ...process.env, ...JSON.parse(envJson) };
  } catch {}
}

let pty;
try {
  // Try loading node-pty from the project's node_modules
  pty = req(join(__dirname, "..", "node_modules", "node-pty"));
} catch {
  try {
    pty = req("node-pty");
  } catch (e) {
    send({ t: "e", msg: `Failed to load node-pty: ${e.message}` });
    process.exit(1);
  }
}

let proc;
try {
  proc = pty.spawn(shell, [], {
    name: "xterm-256color",
    cols,
    rows,
    cwd,
    env,
  });
} catch (e) {
  send({ t: "e", msg: `Failed to spawn PTY: ${e.message}` });
  process.exit(1);
}

send({ t: "r", pid: proc.pid });

// Forward PTY output as base64
proc.onData((data) => {
  send({ t: "d", d: Buffer.from(data).toString("base64") });
});

proc.onExit(({ exitCode, signal }) => {
  send({ t: "x", code: exitCode ?? -1, signal: signal ?? 0 });
  // Give time for the message to flush
  setTimeout(() => process.exit(0), 100);
});

// Read commands from stdin
const rl = createInterface({ input: process.stdin });
rl.on("line", (line) => {
  let msg;
  try {
    msg = JSON.parse(line);
  } catch {
    return;
  }

  switch (msg.t) {
    case "i": // input
      if (msg.d) {
        proc.write(Buffer.from(msg.d, "base64").toString());
      }
      break;
    case "s": // resize
      if (msg.c > 0 && msg.r > 0) {
        proc.resize(msg.c, msg.r);
      }
      break;
    case "c": // close
      proc.kill();
      break;
  }
});

rl.on("close", () => {
  proc.kill();
  setTimeout(() => process.exit(0), 100);
});
