"use client";

import * as React from "react";
import { useRouter } from "next/navigation";
import { Command } from "cmdk";
import { Building2, CalendarDays, Clock, FileText, Hash, Search, Sparkles, UserCog, Users, Wrench, CornerDownLeft, X } from "lucide-react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import type { NavItem, QuickAction } from "@/lib/navigation";
import { Icon } from "@/components/icon";
import { cn } from "@/lib/utils";
import { StatusBadge } from "@/components/ui/badge";
import { looksLikeRef } from "@/lib/refs";

export interface SearchHit {
  group: "Reference" | "Customers" | "Reservations" | "Apartments" | "Workers" | "Contracts" | "Maintenance";
  id: string;
  title: string;
  subtitle?: string;
  href: string;
  badge?: string;
  tone?: string;
  preview?: { kind: string; code: string; status?: string; facts: { label: string; value: string }[] };
}

const GROUP_ICON = { Reference: Hash, Customers: Users, Reservations: CalendarDays, Apartments: Building2, Workers: UserCog, Contracts: FileText, Maintenance: Wrench };
const GROUP_CLS = "[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-2xs [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-wider [&_[cmdk-group-heading]]:text-fg-subtle";
const RECENT_KEY = "lj_recent_searches";

function readRecent(): string[] {
  try {
    const v = JSON.parse(localStorage.getItem(RECENT_KEY) ?? "[]");
    return Array.isArray(v) ? v.filter((s) => typeof s === "string").slice(0, 6) : [];
  } catch {
    return [];
  }
}
function pushRecent(term: string) {
  try {
    const next = [term, ...readRecent().filter((s) => s.toLowerCase() !== term.toLowerCase())].slice(0, 6);
    localStorage.setItem(RECENT_KEY, JSON.stringify(next));
  } catch {}
}

/** Emphasises the typed term inside a result title. */
function Highlight({ text, term }: { text: string; term: string }) {
  const t = term.trim();
  if (!t) return <>{text}</>;
  const i = text.toLowerCase().indexOf(t.toLowerCase());
  if (i < 0) return <>{text}</>;
  return (
    <>
      {text.slice(0, i)}
      <mark className="rounded-sm bg-primary/15 px-0.5 text-primary">{text.slice(i, i + t.length)}</mark>
      {text.slice(i + t.length)}
    </>
  );
}

export function CommandPalette({ open, onOpenChange, nav, quickActions }: { open: boolean; onOpenChange: (o: boolean) => void; nav: NavItem[]; quickActions: QuickAction[] }) {
  const router = useRouter();
  const [q, setQ] = React.useState("");
  const [hits, setHits] = React.useState<SearchHit[]>([]);
  const [loading, setLoading] = React.useState(false);
  const [selected, setSelected] = React.useState("");
  const [recent, setRecent] = React.useState<string[]>([]);

  React.useEffect(() => {
    if (!open) {
      setQ("");
      setHits([]);
    } else setRecent(readRecent());
  }, [open]);

  React.useEffect(() => {
    if (q.trim().length < 2) {
      setHits([]);
      return;
    }
    const ctrl = new AbortController();
    const t = setTimeout(
      async () => {
        setLoading(true);
        try {
          const res = await fetch(`/api/search?q=${encodeURIComponent(q.trim())}`, { signal: ctrl.signal });
          if (res.ok) setHits(((await res.json()) as { hits: SearchHit[] }).hits);
        } catch {
        } finally {
          setLoading(false);
        }
      },
      looksLikeRef(q) ? 80 : 180
    );
    return () => {
      clearTimeout(t);
      ctrl.abort();
    };
  }, [q]);

  const go = (href: string) => {
    if (q.trim().length >= 2) pushRecent(q.trim());
    onOpenChange(false);
    router.push(href);
  };

  const grouped = React.useMemo(() => {
    const m = new Map<string, SearchHit[]>();
    for (const h of hits) m.set(h.group, [...(m.get(h.group) ?? [])]);
    for (const h of hits) m.get(h.group)!.push(h);
    return [...m.entries()];
  }, [hits]);
  const searching = q.trim().length >= 2;
  const previewHit = React.useMemo(() => hits.find((h) => `${h.group}-${h.id}` === selected && h.preview) ?? hits.find((h) => h.preview), [hits, selected]);

  return (
    <DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
      <DialogPrimitive.Portal>
        <DialogPrimitive.Overlay className="fixed inset-0 z-50 bg-stone-950/45 backdrop-blur-[3px] data-[state=open]:animate-fade-in data-[state=closed]:animate-fade-out" />
        <DialogPrimitive.Content className={cn("fixed left-1/2 top-3 z-50 w-[calc(100vw-1rem)] -translate-x-1/2 overflow-hidden rounded-2xl border border-border bg-surface shadow-xl focus:outline-none data-[state=open]:animate-scale-in data-[state=closed]:animate-scale-out sm:top-[10vh] sm:w-[calc(100vw-1.5rem)]", previewHit ? "max-w-3xl" : "max-w-xl")}>
          <DialogPrimitive.Title className="sr-only">Command palette</DialogPrimitive.Title>
          <Command shouldFilter={!searching} label="Global search" value={selected} onValueChange={setSelected}>
            <div className="relative flex items-center gap-2 border-b border-border px-4">
              <Search className={cn("size-4 text-fg-subtle transition-all duration-300", searching && "text-primary", loading && "animate-wiggle")} />
              <Command.Input value={q} onValueChange={setQ} placeholder="Search guests, reservations, phones, IDs, apartments — or type RES-1042" className="h-13 flex-1 bg-transparent text-base outline-none placeholder:text-fg-subtle sm:h-12 sm:text-sm" autoFocus />
              {looksLikeRef(q) ? <span className="hidden rounded-full bg-primary/10 px-2 py-0.5 text-2xs font-semibold text-primary animate-scale-in sm:block">Reference</span> : null}
              {q ? (
                <button type="button" onClick={() => setQ("")} className="rounded-md p-1 text-fg-subtle transition hover:bg-surface-2 hover:text-fg" aria-label="Clear">
                  <X className="size-4" />
                </button>
              ) : (
                <kbd className="hidden rounded border border-border bg-surface-2 px-1.5 py-0.5 font-mono text-2xs text-fg-muted sm:block">esc</kbd>
              )}
              {loading ? (
                <span className="absolute inset-x-0 bottom-[-1px] h-0.5 overflow-hidden" aria-hidden>
                  <span className="route-bar block h-full w-1/4 rounded-full animate-indeterminate" />
                </span>
              ) : null}
            </div>
            <div className={cn("grid", previewHit ? "md:grid-cols-[1fr_280px]" : "")}>
              <Command.List className="max-h-[min(60vh,520px)] overflow-y-auto p-2 scrollbar-thin [&_[cmdk-item]]:animate-[slide-up_0.28s_var(--ease-out-expo)_both]">
                <Command.Empty className="flex flex-col items-center px-3 py-10 text-center text-sm text-fg-muted">
                  <span className="mb-3 flex size-11 items-center justify-center rounded-full bg-surface-2 text-fg-subtle animate-float">
                    <Sparkles className="size-5" />
                  </span>
                  {searching && !loading ? `No results for “${q.trim()}”.` : searching ? "Searching…" : "Type to search, or pick an action."}
                </Command.Empty>
                {!searching ? (
                  <>
                    {recent.length ? (
                      <Command.Group heading="Recent searches" className={GROUP_CLS}>
                        {recent.map((r) => (
                          <Item key={`recent-${r}`} value={`recent ${r}`} onSelect={() => setQ(r)} icon={<Clock className="size-4 text-fg-muted" />}>
                            {r}
                          </Item>
                        ))}
                      </Command.Group>
                    ) : null}
                    <Command.Group heading="Quick actions" className={GROUP_CLS}>
                      {quickActions.map((a) => (
                        <Item key={a.href} onSelect={() => go(a.href)} icon={<Icon name={a.icon} className="size-4 text-fg-muted" />} shortcut={a.shortcut}>
                          {a.label}
                        </Item>
                      ))}
                    </Command.Group>
                    <Command.Group heading="Go to" className={GROUP_CLS}>
                      {nav.map((n) => (
                        <Item key={n.href} onSelect={() => go(n.href)} icon={<Icon name={n.icon} className="size-4 text-fg-muted" />}>
                          {n.label}
                        </Item>
                      ))}
                    </Command.Group>
                  </>
                ) : (
                  grouped.map(([group, items]) => {
                    const GI = GROUP_ICON[group as keyof typeof GROUP_ICON] ?? Search;
                    return (
                      <Command.Group key={group} heading={`${group} · ${items.length}`} className={GROUP_CLS}>
                        {items.map((h) => (
                          <Item key={h.id} value={`${group}-${h.id}`} onSelect={() => go(h.href)} icon={<GI className={cn("size-4", group === "Reference" ? "text-primary" : "text-fg-muted")} />} subtitle={h.subtitle} badge={h.badge} tone={h.tone}>
                            <Highlight text={h.title} term={q} />
                          </Item>
                        ))}
                      </Command.Group>
                    );
                  })
                )}
              </Command.List>
              {previewHit?.preview ? (
                <aside key={previewHit.id} className="hidden border-l border-border bg-surface-2/40 p-4 animate-fade-in md:block">
                  <p className="font-mono text-xs font-semibold text-primary">{previewHit.preview.code}</p>
                  <p className="mt-1 text-sm font-semibold leading-snug">{previewHit.title.replace(`${previewHit.preview.code} · `, "")}</p>
                  {previewHit.subtitle ? <p className="text-xs text-fg-muted">{previewHit.subtitle}</p> : null}
                  {previewHit.preview.status ? (
                    <div className="mt-2">
                      <StatusBadge status={previewHit.preview.status} size="sm" />
                    </div>
                  ) : null}
                  <dl className="stagger-fast mt-3 space-y-1.5 text-xs">
                    {previewHit.preview.facts.map((f) => (
                      <div key={f.label} className="flex justify-between gap-3">
                        <dt className="text-fg-subtle">{f.label}</dt>
                        <dd className="text-right font-medium">{f.value}</dd>
                      </div>
                    ))}
                  </dl>
                  <p className="mt-4 flex items-center gap-1 text-2xs text-fg-subtle">
                    <CornerDownLeft className="size-3" /> Enter to open
                  </p>
                </aside>
              ) : null}
            </div>
            <div className="hidden items-center gap-4 border-t border-border px-4 py-2 text-2xs text-fg-subtle sm:flex">
              <span className="flex items-center gap-1">
                <kbd className="rounded border border-border bg-surface-2 px-1 font-mono">↑</kbd>
                <kbd className="rounded border border-border bg-surface-2 px-1 font-mono">↓</kbd> navigate
              </span>
              <span className="flex items-center gap-1">
                <kbd className="rounded border border-border bg-surface-2 px-1 font-mono">↵</kbd> open
              </span>
              <span className="ml-auto flex items-center gap-1.5">
                {searching ? (
                  <>
                    <span className="live-dot" /> {hits.length} result{hits.length === 1 ? "" : "s"}
                  </>
                ) : (
                  "Search everything in LocaJour"
                )}
              </span>
            </div>
          </Command>
        </DialogPrimitive.Content>
      </DialogPrimitive.Portal>
    </DialogPrimitive.Root>
  );
}

function Item({ children, onSelect, icon, subtitle, badge, tone, shortcut, value }: { children: React.ReactNode; onSelect: () => void; icon?: React.ReactNode; subtitle?: string; badge?: string; tone?: string; shortcut?: string; value?: string }) {
  return (
    <Command.Item value={value ?? (typeof children === "string" ? children : undefined)} onSelect={onSelect} className={cn("group flex cursor-pointer items-center gap-3 rounded-lg px-2 py-2 text-sm text-fg transition-colors duration-150 aria-selected:bg-primary/[0.07] aria-selected:text-fg")}>
      <span className="flex size-7 shrink-0 items-center justify-center rounded-md bg-surface-2 transition-all duration-200 [transition-timing-function:var(--ease-spring)] group-aria-selected:scale-110 group-aria-selected:bg-primary/12 group-aria-selected:[&_svg]:text-primary">{icon}</span>
      <span className="min-w-0 flex-1">
        <span className="block truncate">{children}</span>
        {subtitle ? <span className="block truncate text-xs text-fg-muted">{subtitle}</span> : null}
      </span>
      {badge ? <span className={cn("rounded-full px-1.5 py-0.5 text-2xs", tone === "negative" ? "bg-negative-50 text-negative-700 dark:bg-negative-500/10" : tone === "warning" ? "bg-warning-50 text-warning-700 dark:bg-warning-500/10" : tone === "info" ? "bg-info-50 text-info-700 dark:bg-info-500/10" : "bg-surface-3 text-fg-muted")}>{badge}</span> : null}
      {shortcut ? <kbd className="hidden font-mono text-2xs text-fg-subtle sm:block">{shortcut}</kbd> : null}
      <CornerDownLeft className="hidden size-3.5 text-fg-subtle opacity-0 transition-opacity group-aria-selected:opacity-100 sm:block" />
    </Command.Item>
  );
}
