"use client";

import { Button } from "@hexahost/ui";
import { useQuery } from "@tanstack/react-query";
import { useTranslations } from "next-intl";
import { useCallback, useEffect, useRef, useState } from "react";
import { ApiError } from "@/lib/api/auth";
import {
  createConsoleWebSocket,
  getConsoleToken,
  sendConsoleCommand,
} from "@/lib/api/console";
import { getServer } from "@/lib/api/servers";
import { parseAnsiLine } from "@/lib/utils/ansi";

const MAX_LOG_LINES = 500;

interface LogLine {
  id: number;
  segments: ReturnType<typeof parseAnsiLine>;
}

interface ServerConsoleProps {
  serverId: string;
}

const inputClassName =
  "flex h-10 w-full rounded-md border border-zinc-300 bg-white px-3 font-mono text-sm text-zinc-900 placeholder:text-zinc-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100";

export function ServerConsole({ serverId }: ServerConsoleProps) {
  const t = useTranslations("servers.console");
  const [lines, setLines] = useState<LogLine[]>([]);
  const [command, setCommand] = useState("");
  const [connected, setConnected] = useState(false);
  const [connecting, setConnecting] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const lineIdRef = useRef(0);
  const socketRef = useRef<WebSocket | null>(null);
  const logEndRef = useRef<HTMLDivElement>(null);
  const autoScrollRef = useRef(true);

  const { data: server } = useQuery({
    queryKey: ["server", serverId],
    queryFn: () => getServer(serverId),
  });

  const appendLine = useCallback((line: string) => {
    lineIdRef.current += 1;
    setLines((prev) => {
      const next = [
        ...prev,
        { id: lineIdRef.current, segments: parseAnsiLine(line) },
      ];
      return next.length > MAX_LOG_LINES ? next.slice(-MAX_LOG_LINES) : next;
    });
  }, []);

  useEffect(() => {
    if (autoScrollRef.current) {
      logEndRef.current?.scrollIntoView({ behavior: "smooth" });
    }
  }, [lines]);

  useEffect(() => {
    if (!server || server.status !== "RUNNING") {
      return;
    }

    let cancelled = false;

    async function connect() {
      setConnecting(true);
      setError(null);

      try {
        const { token } = await getConsoleToken(serverId);
        if (cancelled) {
          return;
        }

        const socket = createConsoleWebSocket(serverId, token, {
          onOpen: () => {
            if (!cancelled) {
              setConnected(true);
              setConnecting(false);
            }
          },
          onClose: () => {
            setConnected(false);
            setConnecting(false);
          },
          onError: () => {
            if (!cancelled) {
              setError(t("errors.connectionFailed"));
              setConnecting(false);
            }
          },
          onLine: (line) => {
            appendLine(line);
          },
        });

        socketRef.current = socket;
      } catch (err) {
        if (!cancelled) {
          setError(
            err instanceof ApiError ? (err.detail ?? err.title) : t("errors.connectionFailed"),
          );
          setConnecting(false);
        }
      }
    }

    void connect();

    return () => {
      cancelled = true;
      socketRef.current?.close();
      socketRef.current = null;
      setConnected(false);
      setConnecting(false);
    };
  }, [server, serverId, appendLine, t]);

  function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
    const trimmed = command.trim();
    if (!trimmed || !socketRef.current || socketRef.current.readyState !== WebSocket.OPEN) {
      return;
    }
    sendConsoleCommand(socketRef.current, trimmed);
    appendLine(`> ${trimmed}`);
    setCommand("");
  }

  if (!server) {
    return <p className="text-sm text-zinc-600 dark:text-zinc-400">{t("loading")}</p>;
  }

  if (server.status !== "RUNNING") {
    return (
      <div className="rounded-lg border border-amber-200 bg-amber-50 p-4 dark:border-amber-900 dark:bg-amber-950/40">
        <p className="text-sm text-amber-800 dark:text-amber-200">{t("notRunning")}</p>
      </div>
    );
  }

  return (
    <div className="space-y-4">
      <div className="flex items-center justify-between gap-4">
        <p className="text-sm text-zinc-600 dark:text-zinc-400">
          {connecting
            ? t("connecting")
            : connected
              ? t("connected")
              : t("disconnected")}
        </p>
        <Button
          size="sm"
          variant="secondary"
          onClick={() => setLines([])}
          disabled={lines.length === 0}
        >
          {t("clear")}
        </Button>
      </div>

      {error ? (
        <p className="text-sm text-red-600 dark:text-red-400" role="alert">
          {error}
        </p>
      ) : null}

      <div
        className="h-96 overflow-y-auto rounded-lg border border-zinc-200 bg-zinc-950 p-3 font-mono text-xs leading-relaxed dark:border-zinc-800"
        onScroll={(event) => {
          const target = event.currentTarget;
          const atBottom = target.scrollHeight - target.scrollTop - target.clientHeight < 32;
          autoScrollRef.current = atBottom;
        }}
      >
        <pre className="whitespace-pre-wrap break-words text-zinc-100">
          <code>
            {lines.map((line) => (
              <div key={line.id}>
                {line.segments.map((segment, index) => (
                  <span key={index} className={segment.className}>
                    {segment.text}
                  </span>
                ))}
              </div>
            ))}
            <div ref={logEndRef} />
          </code>
        </pre>
      </div>

      <form onSubmit={handleSubmit} className="flex gap-2">
        <input
          type="text"
          value={command}
          onChange={(event) => setCommand(event.target.value)}
          placeholder={t("commandPlaceholder")}
          className={inputClassName}
          disabled={!connected}
          autoComplete="off"
          spellCheck={false}
        />
        <Button type="submit" disabled={!connected || !command.trim()}>
          {t("send")}
        </Button>
      </form>
    </div>
  );
}
