// SshTerminal — xterm.js WebSocket bridge

function SshTerminal({ instanceId, commandRef }) {
  const containerRef = React.useRef(null);
  const termRef      = React.useRef(null);
  const wsRef        = React.useRef(null);
  const fitRef       = React.useRef(null);
  const [status, setStatus] = React.useState("connecting");
  const captureRef   = React.useRef({ buffer: [], resolve: null, timeout: null });

  React.useEffect(() => {
    if (!containerRef.current) return;

    const term = new window.Terminal({
      cursorBlink: true,
      fontFamily:  "JetBrains Mono, ui-monospace, monospace",
      fontSize:    13,
      theme: {
        background: "#0b0c0e",
        foreground: "#eceae4",
        cursor:     "#6fa3d0",
      },
      scrollback: 5000,
    });
    termRef.current = term;

    const fitAddon = new window.FitAddon.FitAddon();
    fitRef.current = fitAddon;
    term.loadAddon(fitAddon);
    term.loadAddon(new window.WebLinksAddon.WebLinksAddon());
    term.open(containerRef.current);
    fitAddon.fit();

    const proto = location.protocol === "https:" ? "wss:" : "ws:";
    const ws = new WebSocket(`${proto}//${location.host}/ws/ssh/${instanceId}`);
    wsRef.current = ws;
    ws.binaryType = "arraybuffer";

    if (commandRef) {
      commandRef.current = {
        send: (cmd) => {
          if (ws.readyState === WebSocket.OPEN) ws.send(cmd + "\n");
        },
        runAndCapture: (cmd) => {
          return new Promise((resolve) => {
            captureRef.current.buffer = [];
            captureRef.current.resolve = resolve;
            const settle = () => {
              const cap = captureRef.current;
              if (cap.resolve) {
                const result = cap.buffer.join("").replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "").trim();
                cap.buffer = [];
                cap.resolve(result);
                cap.resolve = null;
              }
            };
            captureRef.current.timeout = setTimeout(settle, 2500);
            if (ws.readyState === WebSocket.OPEN) ws.send(cmd + "\n");
          });
        },
      };
    }

    ws.onopen = () => setStatus("connected");
    ws.onclose = (e) => {
      const msgs = {
        4001: "NOT AUTHENTICATED",
        4002: "SSH CONNECTION FAILED",
        4003: "NO SSH KEY — GENERATE A KEY IN SETTINGS",
        4004: "INSTANCE NOT FOUND",
      };
      setStatus("disconnected");
      term.writeln(`\r\n\x1b[31m[SEIS] DISCONNECTED${msgs[e.code] ? ": " + msgs[e.code] : ""}\x1b[0m`);
    };
    ws.onerror = () => setStatus("error");
    ws.onmessage = (e) => {
      let text;
      if (e.data instanceof ArrayBuffer) {
        term.write(new Uint8Array(e.data));
        text = new TextDecoder().decode(e.data);
      } else {
        term.write(e.data);
        text = e.data;
      }
      if (captureRef.current.resolve) {
        captureRef.current.buffer.push(text);
        clearTimeout(captureRef.current.timeout);
        captureRef.current.timeout = setTimeout(() => {
          const cap = captureRef.current;
          if (cap.resolve) {
            const result = cap.buffer.join("").replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "").trim();
            cap.buffer = [];
            cap.resolve(result);
            cap.resolve = null;
          }
        }, 2500);
      }
    };

    term.onData(data => {
      if (ws.readyState === WebSocket.OPEN) {
        ws.send(data);
        // Reset capture timeout on user keystrokes
        if (captureRef.current.resolve) {
          clearTimeout(captureRef.current.timeout);
          captureRef.current.timeout = setTimeout(() => {
            const cap = captureRef.current;
            if (cap.resolve) {
              const result = cap.buffer.join("").replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "").trim();
              cap.buffer = [];
              cap.resolve(result);
              cap.resolve = null;
            }
          }, 2500);
        }
      }
    });

    const resizeObserver = new ResizeObserver(() => {
      fitAddon.fit();
      if (ws.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify({ type: "resize", cols: term.cols, rows: term.rows }));
      }
    });
    resizeObserver.observe(containerRef.current);

    return () => {
      resizeObserver.disconnect();
      ws.close();
      term.dispose();
    };
  }, [instanceId]);

  const exportTerminal = () => {
    const term = termRef.current;
    if (!term) return;
    const lines = [];
    const rows = term.buffer.active.baseY + term.buffer.active.length;
    for (let r = 0; r < rows; r++) {
      const line = term.buffer.active.getLine(r);
      if (line) lines.push(line.translateToString());
    }
    const text = lines.join("\n");
    const blob = new Blob([text], { type: "text/plain" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `terminal-${instanceId}-${new Date().toISOString().slice(0,10)}.txt`;
    a.click();
    URL.revokeObjectURL(url);
  };

  const statusColor = { connecting: "var(--warn)", connected: "var(--ok)", disconnected: "var(--muted)", error: "var(--danger)" }[status] || "var(--muted)";

  return (
    <div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
      <div style={{ padding: "6px 12px", borderBottom: "1px solid var(--rule-soft)", display: "flex", alignItems: "center", gap: 10, background: "var(--bg-2)", flexShrink: 0 }}>
        <span style={{ width: 8, height: 8, borderRadius: "50%", background: statusColor, display: "inline-block" }} />
        <span className="micro" style={{ color: statusColor }}>{status.toUpperCase()}</span>
        <span className="micro" style={{ color: "var(--muted)", marginLeft: "auto" }}>{instanceId}</span>
        <button className="micro" style={{ cursor: "pointer", color: "var(--muted)" }} onClick={exportTerminal} title="Export terminal buffer">⟱ EXPORT</button>
        {status === "disconnected" || status === "error" ? (
          <button className="micro" style={{ cursor: "pointer", color: "var(--accent)", marginLeft: 8 }}
            onClick={() => window.location.reload()}>RECONNECT ↩</button>
        ) : null}
      </div>
      <div ref={containerRef} style={{ flex: 1, minHeight: 0 }} />
    </div>
  );
}

window.SEIS_TERMINAL = { SshTerminal };
