import { FitAddon } from "@xterm/addon-fit";
import { WebLinksAddon } from "@xterm/addon-web-links";
import { Terminal } from "@xterm/xterm";
import { html, LitElement } from "lit";
import { customElement, state } from "lit/decorators.js";
import "@xterm/xterm/css/xterm.css";

@customElement("jae-terminal-panel")
export class JaeTerminalPanel extends LitElement {
	@state() private connected = false;
	@state() private connecting = false;

	private term: Terminal | null = null;
	private fitAddon: FitAddon | null = null;
	private ws: WebSocket | null = null;
	private container: HTMLElement | null = null;
	private resizeObs: ResizeObserver | null = null;

	createRenderRoot() {
		return this;
	}

	override connectedCallback() {
		super.connectedCallback();
		this.style.display = "flex";
		this.style.flexDirection = "column";
		this.style.height = "100%";
		this.style.minHeight = "0";
	}

	override disconnectedCallback() {
		super.disconnectedCallback();
		this.destroyTerminal();
	}

	private destroyTerminal() {
		this.resizeObs?.disconnect();
		this.ws?.close();
		this.term?.dispose();
		this.term = null;
		this.ws = null;
	}

	async connect() {
		if (this.connected || this.connecting) return;
		this.connecting = true;
		this.requestUpdate();
		await this.updateComplete;

		this.container = this.querySelector("#xterm-container") as HTMLElement;
		if (!this.container) {
			this.connecting = false;
			return;
		}

		const isDark = document.documentElement.classList.contains("dark");
		this.term = new Terminal({
			cursorBlink: true,
			fontFamily: '"Fira Code", "Cascadia Code", monospace',
			fontSize: 13,
			theme: isDark
				? { background: "#09090b", foreground: "#e4e4e7", cursor: "#a1a1aa" }
				: { background: "#ffffff", foreground: "#18181b", cursor: "#52525b" },
		});
		this.fitAddon = new FitAddon();
		this.term.loadAddon(this.fitAddon);
		this.term.loadAddon(new WebLinksAddon());
		this.term.open(this.container);
		this.fitAddon.fit();

		this.ws = new WebSocket("ws://localhost:7700/ws/terminal");
		this.ws.onopen = () => {
			this.connected = true;
			this.connecting = false;
			this.requestUpdate();
		};
		this.ws.onclose = () => {
			this.connected = false;
			this.connecting = false;
			this.term?.write("\r\n\x1b[31m[disconnected]\x1b[0m\r\n");
			this.requestUpdate();
		};
		this.ws.onerror = () => {
			this.connecting = false;
			this.connected = false;
			this.term?.write("\r\n\x1b[31m[connection error - is terminal server running?]\x1b[0m\r\n");
			this.requestUpdate();
		};
		this.ws.onmessage = (e) => {
			const m = JSON.parse(e.data);
			if (m.type === "data") this.term?.write(m.data);
			if (m.type === "exit") this.term?.write(`\r\n\x1b[33m[process exited: ${m.code}]\x1b[0m\r\n`);
		};
		this.term.onData((d) => {
			this.ws?.send(JSON.stringify({ type: "input", data: d }));
		});

		this.resizeObs = new ResizeObserver(() => {
			requestAnimationFrame(() => {
				this.fitAddon?.fit();
				if (this.term && this.ws?.readyState === WebSocket.OPEN) {
					this.ws.send(JSON.stringify({ type: "resize", cols: this.term.cols, rows: this.term.rows }));
				}
			});
		});
		this.resizeObs.observe(this.container);
	}

	disconnect() {
		this.destroyTerminal();
		this.connected = false;
		this.requesting = false;
		this.requestUpdate();
	}

	override render() {
		return html`
      <div class="flex items-center gap-2 px-2 py-1 border-b border-border shrink-0 bg-muted/30" style="height:34px">
        <div class="w-2 h-2 rounded-full ${this.connected ? "bg-green-500" : "bg-red-500"}"></div>
        <span class="text-xs font-mono text-muted-foreground">bash</span>
        <div class="flex-1"></div>
        ${
				!this.connected && !this.connecting
					? html`<button class="text-xs px-2 py-0.5 rounded bg-primary text-primary-foreground hover:opacity-90" @click=${() => this.connect()}>Connect</button>`
					: html``
			}
        ${this.connecting ? html`<span class="text-xs text-muted-foreground">Connecting...</span>` : html``}
        ${this.connected ? html`<button class="text-xs px-2 py-0.5 rounded bg-secondary text-secondary-foreground hover:opacity-90" @click=${() => this.disconnect()}>Kill</button>` : html``}
      </div>
      <div id="xterm-container" class="flex-1 overflow-hidden" style="min-height:0;background:#09090b"></div>
    `;
	}
}
