<template>
  <aside class="flex h-[calc(100vh-50px)] flex-col gap-2">
    <header class="flex items-center gap-4">
      <material-symbols:terminal class="size-8" />
      <h1 class="text-2xl max-md:hidden">{{ container.name }}</h1>
      <h2 class="text-sm">Started <RelativeTime :date="container.created" /></h2>
    </header>

    <div ref="host" class="shell flex-1"></div>
  </aside>
</template>

<script setup lang="ts">
import { Container } from "@/models/Container";
import "@xterm/xterm/css/xterm.css";
const { container, action } = defineProps<{ container: Container; action: "attach" | "exec" }>();

const { Terminal } = await import("@xterm/xterm");
const { WebLinksAddon } = await import("@xterm/addon-web-links");
const { FitAddon } = await import("@xterm/addon-fit");

const host = useTemplateRef<HTMLDivElement>("host");
const terminal = new Terminal({
  cursorBlink: true,
  cursorStyle: "block",
});
terminal.loadAddon(new WebLinksAddon());
const fitAddon = new FitAddon();
terminal.loadAddon(fitAddon);

let ws: WebSocket | null = null;

function sendEvent(type: "userinput" | "resize", data?: string, width?: number, height?: number) {
  if (!ws || ws.readyState !== WebSocket.OPEN) return;

  const event: { type: string; data?: string; width?: number; height?: number } = { type };
  if (data !== undefined) event.data = data;
  if (width !== undefined) event.width = width;
  if (height !== undefined) event.height = height;

  ws.send(JSON.stringify(event));
}

onMounted(() => {
  terminal.open(host.value!);
  fitAddon.fit();

  ws = new WebSocket(withBase(`/api/hosts/${container.host}/containers/${container.id}/${action}`));
  ws.onopen = () => {
    terminal.writeln(`Attaching to ${container.name} 🚀`);

    // Send initial resize event
    sendEvent("resize", undefined, terminal.cols, terminal.rows);

    if (action === "attach") {
      sendEvent("userinput", "\r");
    }

    terminal.onData((data) => {
      sendEvent("userinput", data);
    });

    // Handle terminal resize
    terminal.onResize(({ cols, rows }) => {
      sendEvent("resize", undefined, cols, rows);
    });

    terminal.focus();
  };

  ws.onmessage = (event) => terminal.write(event.data);
  ws.addEventListener("close", () => {
    terminal.writeln("⚠️ Connection closed");
  });

  // Handle window resize
  const { width, height } = useWindowSize();
  watch([width, height], () => {
    requestAnimationFrame(() => {
      fitAddon.fit();
    });
  });
});

onUnmounted(() => {
  console.log("Closing WebSocket");
  terminal.dispose();
  ws?.close();
});
</script>
<style scoped>
@reference "@/main.css";

.shell {
  & :deep(.terminal) {
    @apply overflow-hidden rounded border p-2;
    &:is(.focus) {
      @apply border-primary;
    }
  }

  & :deep(.xterm-viewport) {
    @apply bg-base-200!;
  }

  & :deep(.xterm-rows) {
    @apply text-base-content;
  }

  & :deep(.xterm-cursor-block.xterm-cursor-blink) {
    animation-name: blink !important;
  }
}

@keyframes blink {
  0% {
    background-color: var(--color-base-content);
    color: #000000;
  }

  50% {
    background-color: inherit;
    color: var(--color-base-content);
  }
}
</style>
