"use client";

import { Command } from "cmdk";
import { useQueryClient } from "@tanstack/react-query";
import { useSession, signIn, signOut } from "next-auth/react";
import { useRouter } from "next/navigation";
import { useCallback, useEffect, useMemo, useState } from "react";
import { toast } from "sonner";

type Mod = { filename: string; displayName: string; modId: string };
type Snapshot = { dirName: string; name: string };
type Backup = { name: string };
type PlayerData = {
  ops: { name: string }[];
  whitelist: { name: string }[];
  banned: { name: string }[];
};

const TABS: { value: string; label: string }[] = [
  { value: "server", label: "Server" },
  { value: "players", label: "Players" },
  { value: "chat", label: "Chat" },
  { value: "mods", label: "Mods" },
  { value: "backups", label: "Backups" },
  { value: "logs", label: "Logs" },
];

function toggleTheme() {
  const html = document.documentElement;
  const dark = html.classList.contains("dark");
  const next = dark ? "light" : "dark";
  html.classList.toggle("dark", next === "dark");
  html.classList.toggle("light", next === "light");
  html.style.colorScheme = next;
  localStorage.setItem("theme", next);
}

export function CommandPalette() {
  const [open, setOpen] = useState(false);
  const router = useRouter();
  const queryClient = useQueryClient();
  const { data: session } = useSession();
  const authed = !!session;

  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if ((e.key === "k" || e.key === "K") && (e.metaKey || e.ctrlKey)) {
        e.preventDefault();
        setOpen((v) => !v);
      }
      if (e.key === "Escape") setOpen(false);
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  const close = useCallback(() => setOpen(false), []);
  const run = useCallback(
    (fn: () => void | Promise<void>) => () => {
      close();
      Promise.resolve(fn()).catch(() => {});
    },
    [close]
  );

  const goToTab = (tab: string) => {
    if (typeof window === "undefined") return;
    if (!window.location.pathname.startsWith("/admin")) {
      router.push(`/admin#${tab}`);
    } else {
      window.location.hash = tab;
      // force AdminTabs to sync by dispatching a hashchange
      window.dispatchEvent(new HashChangeEvent("hashchange"));
    }
  };

  const mods = (queryClient.getQueryData<Mod[]>(["mods"]) || []).slice(0, 20);
  const snapshots =
    (queryClient.getQueryData<Snapshot[]>(["snapshots"]) || []).slice(0, 10);
  const backups = (queryClient.getQueryData<Backup[]>(["backups"]) || []).slice(0, 10);
  const players = queryClient.getQueryData<PlayerData>(["players"]);
  const playerList = useMemo(() => {
    if (!players) return [] as { name: string; group: string }[];
    const out: { name: string; group: string }[] = [];
    players.ops.forEach((p) => out.push({ name: p.name, group: "Ops" }));
    players.whitelist.forEach((p) => out.push({ name: p.name, group: "Whitelist" }));
    return out.slice(0, 20);
  }, [players]);

  const serverAction = async (act: "start" | "stop" | "restart") => {
    const res = await fetch(`/api/server/${act}`, { method: "POST" });
    if (res.ok) {
      toast.success(`${act} command sent`);
      queryClient.invalidateQueries({ queryKey: ["status"] });
    } else {
      const data = await res.json().catch(() => ({}));
      toast.error(`${act} failed`, { description: data?.error });
    }
  };

  const createBackup = async () => {
    toast.loading("Creating backup...", { id: "cmdk-backup" });
    const res = await fetch("/api/backups", { method: "POST" });
    const data = await res.json();
    toast.dismiss("cmdk-backup");
    if (res.ok) {
      toast.success(data.message || "Backup created");
      queryClient.invalidateQueries({ queryKey: ["backups"] });
    } else {
      toast.error("Backup failed", { description: data.error });
    }
  };

  if (!open) return null;

  return (
    <div
      className="fixed inset-0 z-50 flex items-start justify-center pt-[10vh] bg-black/50 backdrop-blur-sm"
      role="dialog"
      aria-modal="true"
      aria-label="Command palette"
      onClick={(e) => {
        if (e.target === e.currentTarget) close();
      }}
    >
      <Command
        label="Command Menu"
        className="w-[95%] max-w-lg rounded-xl border border-border bg-popover text-popover-foreground shadow-2xl overflow-hidden"
      >
        <div className="border-b border-border px-3 py-2.5 flex items-center gap-2">
          <span aria-hidden className="text-muted-foreground text-sm">⌘K</span>
          <Command.Input
            autoFocus
            placeholder="Type a command or search..."
            className="flex-1 bg-transparent outline-none text-sm placeholder:text-muted-foreground"
          />
        </div>
        <Command.List className="max-h-[60vh] overflow-y-auto p-1.5">
          <Command.Empty className="px-3 py-6 text-center text-sm text-muted-foreground">
            No matches.
          </Command.Empty>

          <Command.Group heading="Navigate" className="text-xs text-muted-foreground px-1.5 pt-1.5">
            <Item onSelect={run(() => router.push("/"))}>Go to Home</Item>
            {authed ? (
              TABS.map((t) => (
                <Item key={t.value} onSelect={run(() => goToTab(t.value))}>
                  Admin · {t.label}
                </Item>
              ))
            ) : (
              <Item onSelect={run(() => signIn())}>Log in</Item>
            )}
          </Command.Group>

          <Command.Group heading="Appearance" className="text-xs text-muted-foreground px-1.5 pt-1.5">
            <Item onSelect={run(() => toggleTheme())}>Toggle theme</Item>
          </Command.Group>

          {authed && (
            <>
              <Command.Group heading="Server" className="text-xs text-muted-foreground px-1.5 pt-1.5">
                <Item onSelect={run(() => serverAction("start"))}>Start server</Item>
                <Item onSelect={run(() => serverAction("restart"))}>Restart server</Item>
                <Item onSelect={run(() => serverAction("stop"))}>Stop server</Item>
                <Item onSelect={run(() => createBackup())}>Create world backup now</Item>
                <Item
                  onSelect={run(() => {
                    queryClient.invalidateQueries({ queryKey: ["mod-updates"] });
                    toast.success("Checking Modrinth for updates...");
                  })}
                >
                  Check for mod updates
                </Item>
                <Item onSelect={run(() => signOut({ callbackUrl: "/" }))}>
                  Log out
                </Item>
              </Command.Group>

              {mods.length > 0 && (
                <Command.Group heading="Installed Mods" className="text-xs text-muted-foreground px-1.5 pt-1.5">
                  {mods.map((m) => (
                    <Item
                      key={m.filename}
                      value={`mod ${m.displayName} ${m.modId} ${m.filename}`}
                      onSelect={run(() => goToTab("mods"))}
                    >
                      <span className="truncate">{m.displayName}</span>
                    </Item>
                  ))}
                </Command.Group>
              )}

              {playerList.length > 0 && (
                <Command.Group heading="Players" className="text-xs text-muted-foreground px-1.5 pt-1.5">
                  {playerList.map((p, i) => (
                    <Item
                      key={`${p.group}-${p.name}-${i}`}
                      value={`player ${p.name} ${p.group}`}
                      onSelect={run(() => goToTab("players"))}
                    >
                      <span>{p.name}</span>
                      <span className="ml-auto text-[10px] text-muted-foreground uppercase">
                        {p.group}
                      </span>
                    </Item>
                  ))}
                </Command.Group>
              )}

              {snapshots.length > 0 && (
                <Command.Group heading="Snapshots" className="text-xs text-muted-foreground px-1.5 pt-1.5">
                  {snapshots.map((s) => (
                    <Item
                      key={s.dirName}
                      value={`snapshot ${s.name} ${s.dirName}`}
                      onSelect={run(() => goToTab("mods"))}
                    >
                      <span className="truncate">{s.name}</span>
                    </Item>
                  ))}
                </Command.Group>
              )}

              {backups.length > 0 && (
                <Command.Group heading="Backups" className="text-xs text-muted-foreground px-1.5 pt-1.5">
                  {backups.map((b) => (
                    <Item
                      key={b.name}
                      value={`backup ${b.name}`}
                      onSelect={run(() => goToTab("backups"))}
                    >
                      <span className="truncate">{b.name}</span>
                    </Item>
                  ))}
                </Command.Group>
              )}
            </>
          )}
        </Command.List>
        <div className="border-t border-border px-3 py-1.5 text-[10px] text-muted-foreground flex justify-between">
          <span>↑↓ navigate · ↵ select · esc close</span>
          <span aria-hidden>⌘K</span>
        </div>
      </Command>
    </div>
  );
}

function Item({
  children,
  onSelect,
  value,
}: {
  children: React.ReactNode;
  onSelect: () => void;
  value?: string;
}) {
  return (
    <Command.Item
      onSelect={onSelect}
      value={value}
      className="flex items-center gap-2 rounded-md px-2.5 py-2 text-sm cursor-pointer data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground aria-selected:bg-accent aria-selected:text-accent-foreground"
    >
      {children}
    </Command.Item>
  );
}
