"use client";

import * as DialogPrimitive from "@radix-ui/react-dialog";
import { generateText } from "ai";
import {
  ArrowRight,
  CheckSquare,
  Command,
  FileText,
  FolderKanban,
  LayoutGrid,
  Loader2,
  Search,
  Settings,
  Sparkles,
  SquarePen,
} from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import * as React from "react";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
import { cn } from "@/lib/utils";

import {
  createOpenAIClient,
  GENERAL_SYSTEM_PROMPT,
  selectOpenAIModel,
} from "@tasks/ai";

const RECENT_KEY = "tasks-command-palette-recent-v1";

type PaletteMode = "default" | "ai" | "nav";

export type ResultKind = "object" | "action" | "page" | "ai";

export type PaletteResult = {
  id: string;
  kind: ResultKind;
  title: string;
  subtitle?: string;
  icon: React.ComponentType<{ className?: string }>;
  href?: string;
  onSelect?: () => void;
  group: string;
};

function loadRecent(): string[] {
  if (typeof window === "undefined") return [];
  try {
    const raw = window.localStorage.getItem(RECENT_KEY);
    if (!raw) return [];
    const parsed = JSON.parse(raw) as unknown;
    return Array.isArray(parsed) ? parsed.filter((x) => typeof x === "string") : [];
  } catch {
    return [];
  }
}

function saveRecent(ids: string[]) {
  try {
    window.localStorage.setItem(RECENT_KEY, JSON.stringify(ids.slice(0, 12)));
  } catch {
    /* ignore */
  }
}

function fuzzyScore(text: string, query: string): number {
  const t = text.toLowerCase();
  const q = query.toLowerCase().trim();
  if (!q) return 1;
  let qi = 0;
  let bonus = 0;
  for (let i = 0; i < t.length && qi < q.length; i++) {
    if (t[i] === q[qi]) {
      bonus += i === 0 ? 2 : 1;
      qi++;
    }
  }
  if (qi < q.length) return 0;
  return bonus + 1 / t.length;
}

function parseQuery(raw: string): { mode: PaletteMode; body: string } {
  const s = raw.trimStart();
  if (s.startsWith(">")) return { mode: "ai", body: s.slice(1).trim() };
  if (s.startsWith("/")) return { mode: "nav", body: s.slice(1).trim() };
  return { mode: "default", body: raw.trim() };
}

export function CommandPalette() {
  const router = useRouter();
  const params = useParams<{ workspaceSlug?: string }>();
  const workspaceSlug = params?.workspaceSlug ?? "workspace";
  const base = `/${workspaceSlug}`;

  const [open, setOpen] = React.useState(false);
  const [query, setQuery] = React.useState("");
  const [active, setActive] = React.useState(0);
  const [recentIds, setRecentIds] = React.useState<string[]>([]);
  const [aiLoading, setAiLoading] = React.useState(false);
  const [aiReply, setAiReply] = React.useState<string | null>(null);
  const [aiError, setAiError] = React.useState<string | null>(null);

  const inputRef = React.useRef<HTMLInputElement>(null);

  React.useEffect(() => {
    setRecentIds(loadRecent());
  }, [open]);

  React.useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      const isK = e.key === "k" || e.key === "K";
      if (!isK || !(e.metaKey || e.ctrlKey)) return;
      const t = e.target as HTMLElement | null;
      if (t?.closest?.("[data-command-palette-ignore-shortcut]")) return;
      e.preventDefault();
      setOpen((o) => !o);
    };
    document.addEventListener("keydown", onKey, true);
    return () => document.removeEventListener("keydown", onKey, true);
  }, []);

  React.useEffect(() => {
    if (!open) return;
    const id = window.requestAnimationFrame(() => inputRef.current?.focus());
    return () => window.cancelAnimationFrame(id);
  }, [open]);

  React.useEffect(() => {
    if (!open) {
      setAiReply(null);
      setAiError(null);
      setAiLoading(false);
    }
  }, [open]);

  const { mode, body } = parseQuery(query);

  const catalog = React.useMemo((): PaletteResult[] => {
    const objects: PaletteResult[] = [
      {
        id: "obj-1",
        kind: "object",
        title: "Sprint planning",
        subtitle: "Task · Due Friday",
        icon: CheckSquare,
        href: `${base}/lists/sprint`,
        group: "Objects",
      },
      {
        id: "obj-2",
        kind: "object",
        title: "Q1 roadmap",
        subtitle: "Document · Edited 3d ago",
        icon: FileText,
        href: `${base}/docs/roadmap`,
        group: "Objects",
      },
      {
        id: "obj-3",
        kind: "object",
        title: "Product launch",
        subtitle: "Project · 12 members",
        icon: FolderKanban,
        href: `${base}/launch`,
        group: "Objects",
      },
    ];

    const actions: PaletteResult[] = [
      {
        id: "act-create-task",
        kind: "action",
        title: "Create task",
        subtitle: "Add a new task to the current workspace",
        icon: SquarePen,
        group: "Actions",
        onSelect: () => router.push(`${base}?create=task`),
      },
      {
        id: "act-create-doc",
        kind: "action",
        title: "Create document",
        subtitle: "Start a new doc from the template gallery",
        icon: FileText,
        group: "Actions",
        onSelect: () => router.push(`${base}/docs?new=1`),
      },
      {
        id: "act-settings",
        kind: "action",
        title: "Open settings",
        subtitle: "Workspace preferences and integrations",
        icon: Settings,
        group: "Actions",
        onSelect: () => router.push(`${base}/settings`),
      },
    ];

    const pages: PaletteResult[] = [
      {
        id: "page-home",
        kind: "page",
        title: "Workspace home",
        subtitle: "Dashboard",
        icon: LayoutGrid,
        href: base,
        group: "Pages",
      },
      {
        id: "page-docs",
        kind: "page",
        title: "Documents",
        subtitle: "All docs in this workspace",
        icon: FileText,
        href: `${base}/docs`,
        group: "Pages",
      },
      {
        id: "page-boards",
        kind: "page",
        title: "Whiteboards",
        subtitle: "Visual boards",
        icon: FolderKanban,
        href: `${base}/whiteboards`,
        group: "Pages",
      },
    ];

    return [...objects, ...actions, ...pages];
  }, [base, router]);

  const recentResults = React.useMemo(() => {
    if (recentIds.length === 0) return [];
    const map = new Map(catalog.map((c) => [c.id, c]));
    return recentIds
      .map((id) => map.get(id))
      .filter((x): x is PaletteResult => Boolean(x));
  }, [catalog, recentIds]);

  const flatRows = React.useMemo(() => {
    if (mode === "ai") {
      const row: PaletteResult = {
        id: "ai-run",
        kind: "ai",
        title: body ? `Ask AI: ${body}` : "Ask AI (type after >)",
        subtitle: body
          ? "Press Enter to run"
          : "Example: > summarize my week",
        icon: Sparkles,
        group: "AI",
      };
      return [row];
    }
    if (mode === "nav") {
      const pages = catalog.filter((c) => c.kind === "page");
      if (!body) return pages;
      return pages
        .map((p) => ({
          p,
          s: Math.max(
            fuzzyScore(p.title, body),
            fuzzyScore(p.subtitle ?? "", body),
          ),
        }))
        .filter((x) => x.s > 0)
        .sort((a, b) => b.s - a.s)
        .map((x) => x.p);
    }

    const q = body;
    if (!q) {
      if (recentResults.length > 0) {
        const seen = new Set(recentResults.map((r) => r.id));
        const rest = catalog.filter((c) => !seen.has(c.id));
        return [
          ...recentResults.map((r) => ({ ...r, group: "Recent" })),
          ...rest,
        ];
      }
      return catalog;
    }

    const filtered = catalog
      .map((c) => ({
        c,
        s: Math.max(
          fuzzyScore(c.title, q),
          fuzzyScore(c.subtitle ?? "", q),
          fuzzyScore(c.group, q),
        ),
      }))
      .filter((x) => x.s > 0)
      .sort((a, b) => b.s - a.s)
      .map((x) => x.c);

    const ask: PaletteResult = {
      id: "ask-ai",
      kind: "ai",
      title: `Ask AI: ${q}`,
      subtitle: "Answer in the palette",
      icon: Sparkles,
      group: "AI",
    };

    return [...filtered, ask];
  }, [body, catalog, mode, recentResults]);

  React.useEffect(() => {
    setActive(0);
  }, [query, flatRows.length, mode]);

  const pushRecent = React.useCallback((id: string) => {
    setRecentIds((prev) => {
      const next = [id, ...prev.filter((x) => x !== id)];
      saveRecent(next);
      return next;
    });
  }, []);

  const runAi = React.useCallback(async (text: string) => {
    const trimmed = text.trim();
    if (!trimmed) return;
    setAiLoading(true);
    setAiError(null);
    setAiReply(null);
    try {
      const client = createOpenAIClient();
      if (!client.ok) {
        setAiError(
          "Add OPENAI_API_KEY or NEXT_PUBLIC_OPENAI_API_KEY to use AI in the command palette.",
        );
        return;
      }
      const model = selectOpenAIModel(client.provider, "gpt-4o-mini");
      const { text: out } = await generateText({
        model,
        system: GENERAL_SYSTEM_PROMPT,
        prompt: trimmed,
      });
      setAiReply(out);
    } catch (e) {
      setAiError((e as Error).message ?? "AI request failed.");
    } finally {
      setAiLoading(false);
    }
  }, []);

  const execute = React.useCallback(
    (row: PaletteResult) => {
      if (row.kind === "ai" && row.id === "ask-ai") {
        void runAi(body);
        pushRecent(row.id);
        return;
      }
      if (row.kind === "ai" && row.id === "ai-run") {
        void runAi(body);
        return;
      }
      if (row.href) {
        router.push(row.href);
        pushRecent(row.id);
        setOpen(false);
        setQuery("");
        return;
      }
      if (row.onSelect) {
        row.onSelect();
        pushRecent(row.id);
        setOpen(false);
        setQuery("");
      }
    },
    [body, pushRecent, router, runAi],
  );

  const onKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === "ArrowDown") {
      e.preventDefault();
      setActive((i) => (flatRows.length ? (i + 1) % flatRows.length : 0));
    } else if (e.key === "ArrowUp") {
      e.preventDefault();
      setActive((i) =>
        flatRows.length ? (i - 1 + flatRows.length) % flatRows.length : 0,
      );
    } else if (e.key === "Enter") {
      e.preventDefault();
      const row = flatRows[active];
      if (row) execute(row);
    }
  };

  return (
    <DialogPrimitive.Root open={open} onOpenChange={setOpen}>
      <DialogPrimitive.Portal>
        <DialogPrimitive.Overlay className="fixed inset-0 z-[200] bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" />
        <DialogPrimitive.Content
          className={cn(
            "fixed left-1/2 top-[12vh] z-[201] w-[min(720px,calc(100vw-2rem))] -translate-x-1/2 rounded-2xl border border-border bg-popover p-0 shadow-2xl outline-none",
            "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
          )}
          onKeyDown={onKeyDown}
          onOpenAutoFocus={(ev) => ev.preventDefault()}
        >
          <div className="flex items-center gap-3 border-b border-border px-4 py-3">
            <Search className="size-5 shrink-0 text-muted-foreground" />
            <Input
              ref={inputRef}
              value={query}
              onChange={(e) => setQuery(e.target.value)}
              placeholder={
                mode === "ai"
                  ? "AI command…"
                  : mode === "nav"
                    ? "Jump to page…"
                    : "Search or run a command…"
              }
              className="h-11 border-0 bg-transparent px-0 text-base shadow-none focus-visible:ring-0"
            />
            <Badge variant="outline" className="hidden shrink-0 gap-1 sm:inline-flex">
              <Command className="size-3" />
              K
            </Badge>
          </div>

          <div className="px-4 pb-2 pt-1 text-[11px] text-muted-foreground">
            <span className="font-medium text-foreground/80">Modes:</span>{" "}
            <kbd className="rounded border border-border bg-muted px-1 py-0.5 font-mono text-[10px]">
              &gt;
            </kbd>{" "}
            AI ·{" "}
            <kbd className="rounded border border-border bg-muted px-1 py-0.5 font-mono text-[10px]">
              /
            </kbd>{" "}
            pages
          </div>

          <ScrollArea className="max-h-[min(420px,60vh)]">
            <div className="px-2 pb-3 pt-1">
              {flatRows.length === 0 ? (
                <div className="px-3 py-10 text-center text-sm text-muted-foreground">
                  No matches. Try a shorter query or switch mode with{" "}
                  <kbd className="rounded border px-1 font-mono text-xs">&gt;</kbd>{" "}
                  or{" "}
                  <kbd className="rounded border px-1 font-mono text-xs">/</kbd>.
                </div>
              ) : (
                flatRows.map((row, idx) => {
                  const showHeader =
                    idx === 0 ||
                    flatRows[idx - 1]!.group !== row.group;
                  const Icon = row.icon;
                  const isActive = idx === active;
                  return (
                    <div key={`${row.group}-${row.id}-${idx}`}>
                      {showHeader ? (
                        <>
                          {idx > 0 ? (
                            <Separator className="my-2 opacity-50" />
                          ) : null}
                          <div className="px-2 py-1.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
                            {row.group}
                          </div>
                        </>
                      ) : null}
                      <button
                        type="button"
                        className={cn(
                          "mb-0.5 flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left text-sm transition-colors",
                          isActive
                            ? "bg-primary/12 text-foreground"
                            : "hover:bg-muted/80",
                        )}
                        onClick={() => execute(row)}
                        onMouseEnter={() => setActive(idx)}
                      >
                        <span className="flex size-9 shrink-0 items-center justify-center rounded-md border border-border bg-muted/50">
                          <Icon className="size-4" />
                        </span>
                        <span className="min-w-0 flex-1">
                          <span className="block font-medium leading-tight">
                            {row.title}
                          </span>
                          {row.subtitle ? (
                            <span className="text-xs text-muted-foreground">
                              {row.subtitle}
                            </span>
                          ) : null}
                        </span>
                        {"href" in row && row.href ? (
                          <ArrowRight className="size-4 shrink-0 text-muted-foreground opacity-60" />
                        ) : null}
                      </button>
                    </div>
                  );
                })
              )}
            </div>
          </ScrollArea>

          {(aiLoading || aiReply || aiError) && (
            <div className="border-t border-border px-4 py-3">
              {aiLoading ? (
                <div className="flex items-center gap-2 text-sm text-muted-foreground">
                  <Loader2 className="size-4 animate-spin text-primary" />
                  Thinking…
                </div>
              ) : null}
              {aiError ? (
                <p className="text-sm text-destructive">{aiError}</p>
              ) : null}
              {aiReply ? (
                <div className="max-h-40 overflow-y-auto rounded-lg border border-border bg-muted/30 p-3 text-sm leading-relaxed text-foreground">
                  {aiReply}
                </div>
              ) : null}
              {aiReply || aiError ? (
                <Button
                  type="button"
                  variant="ghost"
                  size="sm"
                  className="mt-2 h-8"
                  onClick={() => {
                    setAiReply(null);
                    setAiError(null);
                  }}
                >
                  Clear
                </Button>
              ) : null}
            </div>
          )}

          <div className="flex items-center justify-between border-t border-border px-4 py-2 text-[11px] text-muted-foreground">
            <span>
              <kbd className="rounded border border-border bg-muted px-1.5 py-0.5 font-mono">
                ↑↓
              </kbd>{" "}
              navigate ·{" "}
              <kbd className="rounded border border-border bg-muted px-1.5 py-0.5 font-mono">
                ↵
              </kbd>{" "}
              run ·{" "}
              <kbd className="rounded border border-border bg-muted px-1.5 py-0.5 font-mono">
                esc
              </kbd>{" "}
              close
            </span>
          </div>

          <DialogPrimitive.Title className="sr-only">
            Command palette
          </DialogPrimitive.Title>
          <DialogPrimitive.Description className="sr-only">
            Search workspace objects, run actions, or ask AI.
          </DialogPrimitive.Description>
        </DialogPrimitive.Content>
      </DialogPrimitive.Portal>
    </DialogPrimitive.Root>
  );
}
