import { useEffect, useRef, useState } from "react";
import { FitAddon } from "@xterm/addon-fit";
import { Terminal } from "@xterm/xterm";
import "@xterm/xterm/css/xterm.css";

type ServerMessage =
  | { type: "ready" }
  | { type: "output"; data: string }
  | { type: "exit"; exitCode: number }
  | { type: "error"; message: string };

export function ShellTerminal({ csrfToken }: { csrfToken: string }) {
  const host = useRef<HTMLDivElement>(null);
  const [generation, setGeneration] = useState(0);
  const [state, setState] = useState<"connecting" | "ready" | "closed" | "error">("connecting");

  useEffect(() => {
    if (!host.current) return;
    const hostElement = host.current;
    const terminal = new Terminal({
      cursorBlink: true,
      cursorStyle: "bar",
      fontFamily: 'ui-monospace, "Cascadia Code", "SFMono-Regular", monospace',
      fontSize: 14,
      lineHeight: 1.25,
      scrollback: 3_000,
      theme: {
        background: "#0b0e0a",
        foreground: "#dce4d6",
        cursor: "#b9e769",
        selectionBackground: "#43542c",
        black: "#10130f",
        green: "#b9e769",
        brightGreen: "#d0f397",
        red: "#ff9b8d"
      }
    });
    const fit = new FitAddon();
    terminal.loadAddon(fit);
    terminal.open(hostElement);
    terminal.writeln("\x1b[38;2;185;231;105mPi Car Companion shell\x1b[0m");
    terminal.writeln("Restricted local session running as pi-companion.\r\n");

    const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
    const socket = new WebSocket(`${protocol}//${window.location.host}/api/system/shell`);
    const sendSize = () => {
      if (socket.readyState === WebSocket.OPEN) {
        socket.send(JSON.stringify({ type: "resize", cols: terminal.cols, rows: terminal.rows }));
      }
    };
    let resizeFrame: number | null = null;
    let fittedWidth = -1;
    let fittedHeight = -1;
    const fitToHost = () => {
      resizeFrame = null;
      const width = hostElement.clientWidth;
      const height = hostElement.clientHeight;
      if (width <= 0 || height <= 0 || (width === fittedWidth && height === fittedHeight)) return;
      fittedWidth = width;
      fittedHeight = height;
      fit.fit();
      sendSize();
    };
    const scheduleFit = () => {
      if (resizeFrame !== null) window.cancelAnimationFrame(resizeFrame);
      resizeFrame = window.requestAnimationFrame(fitToHost);
    };
    fitToHost();
    socket.addEventListener("open", () => {
      socket.send(JSON.stringify({ type: "authenticate", csrfToken }));
    });
    socket.addEventListener("message", (event) => {
      try {
        const message = JSON.parse(String(event.data)) as ServerMessage;
        if (message.type === "ready") {
          setState("ready");
          fitToHost();
          sendSize();
          terminal.focus();
        } else if (message.type === "output") terminal.write(message.data);
        else if (message.type === "exit") {
          terminal.writeln(`\r\n\x1b[90mShell exited with status ${message.exitCode}.\x1b[0m`);
          setState("closed");
        } else if (message.type === "error") {
          terminal.writeln(`\r\n\x1b[31m${message.message}\x1b[0m`);
          setState("error");
        }
      } catch {
        terminal.writeln("\r\n\x1b[31mInvalid response from the shell service.\x1b[0m");
      }
    });
    socket.addEventListener("close", (event) => {
      if (event.code !== 1000) {
        terminal.writeln(`\r\n\x1b[31mTerminal disconnected${event.reason ? `: ${event.reason}` : "."}\x1b[0m`);
        setState("error");
      } else setState((current) => current === "error" ? "error" : "closed");
    });
    socket.addEventListener("error", () => setState("error"));
    const input = terminal.onData((data) => {
      if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify({ type: "input", data }));
    });
    const resize = new ResizeObserver(scheduleFit);
    resize.observe(hostElement);

    return () => {
      resize.disconnect();
      if (resizeFrame !== null) window.cancelAnimationFrame(resizeFrame);
      input.dispose();
      socket.close(1000, "Terminal tab closed");
      terminal.dispose();
    };
  }, [csrfToken, generation]);

  return (
    <section className="shell-panel" aria-label="Interactive Pi shell">
      <div className="shell-toolbar">
        <div><span className={`shell-state ${state}`} aria-hidden="true" /><strong>{state === "ready" ? "Connected" : state === "connecting" ? "Connecting" : "Disconnected"}</strong><small>pi-companion@localhost</small></div>
        {state !== "ready" && <button className="secondary-button text-button" onClick={() => { setState("connecting"); setGeneration((value) => value + 1); }}>Reconnect</button>}
      </div>
      <div className="terminal-host" ref={host} />
      <p className="settings-note">This authenticated terminal runs locally as the restricted <code>pi-companion</code> service account. Sessions close when you leave this tab and expire after 30 minutes.</p>
    </section>
  );
}
