"use client";

import * as DialogPrimitive from "@radix-ui/react-dialog";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { CornerDownLeft, Search, X } from "lucide-react";
import { useParams } from "next/navigation";

import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { ScrollArea } from "@/components/ui/scroll-area";
import { usePanelStore } from "@/lib/stores/panel-store";
import { useWorkspaceStore } from "@/lib/stores/workspace-store";
import { api } from "@/lib/trpc";
import { cn } from "@/lib/utils";

import {
  SearchResultRow,
  highlightText,
  type SearchResultItem,
} from "./search-result";

const DEBOUNCE_MS = 300;

const MOCK_RESULTS: SearchResultItem[] = [
  {
    id: "10000000-0000-4000-8000-000000000101",
    type: "task",
    title: "Design system audit",
    status: "in_progress",
    parentId: "10000000-0000-4000-8000-000000000002",
    workspaceId: null,
    descriptionSnippet:
      "…align tokens with the new brand palette before the next release.",
    parentBreadcrumb: "Project Alpha > Sprint 1",
  },
  {
    id: "10000000-0000-4000-8000-000000000102",
    type: "document",
    title: "Q1 roadmap",
    status: "open",
    parentId: "10000000-0000-4000-8000-000000000001",
    workspaceId: null,
    descriptionSnippet: "Goals, milestones, and risks for the quarter…",
    parentBreadcrumb: "Project Alpha",
  },
  {
    id: "10000000-0000-4000-8000-000000000103",
    type: "project",
    title: "Mobile launch",
    status: "open",
    parentId: null,
    workspaceId: null,
    descriptionSnippet: "Cross-functional initiative spanning design and eng…",
    parentBreadcrumb: null,
  },
  {
    id: "10000000-0000-4000-8000-000000000104",
    type: "whiteboard",
    title: "Architecture brainstorm",
    status: "open",
    parentId: "10000000-0000-4000-8000-000000000001",
    workspaceId: null,
    descriptionSnippet: "Service diagram and sequence flows for the API layer…",
    parentBreadcrumb: "Project Alpha",
  },
];

const MOCK_RECENT: SearchResultItem[] = MOCK_RESULTS.slice(0, 3);

const GROUP_ORDER = ["task", "document", "project", "whiteboard", "group", "workspace"] as const;

const GROUP_LABEL: Record<string, string> = {
  task: "Tasks",
  document: "Documents",
  project: "Projects",
  whiteboard: "Whiteboards",
  group: "Groups",
  workspace: "Workspace",
};

function isUuid(value: string): boolean {
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
    value,
  );
}

function groupKey(type: string): string {
  if (type === "group") return "project";
  return type;
}

function sortGroupEntries(
  map: Map<string, SearchResultItem[]>,
): { key: string; label: string; items: SearchResultItem[] }[] {
  const out: { key: string; label: string; items: SearchResultItem[] }[] = [];
  for (const k of GROUP_ORDER) {
    const items = map.get(k);
    if (items?.length) {
      out.push({
        key: k,
        label: GROUP_LABEL[k] ?? k,
        items,
      });
    }
  }
  for (const [k, items] of map) {
    if (!GROUP_ORDER.includes(k as (typeof GROUP_ORDER)[number]) && items.length) {
      out.push({
        key: k,
        label: GROUP_LABEL[k] ?? k,
        items,
      });
    }
  }
  return out;
}

function groupResults(rows: SearchResultItem[]) {
  const map = new Map<string, SearchResultItem[]>();
  for (const r of rows) {
    const g = groupKey(r.type);
    if (!map.has(g)) map.set(g, []);
    map.get(g)!.push(r);
  }
  return sortGroupEntries(map);
}

function useDebouncedValue<T>(value: T, delay: number): T {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const t = window.setTimeout(() => setDebounced(value), delay);
    return () => window.clearTimeout(t);
  }, [value, delay]);
  return debounced;
}

function ResultSkeleton() {
  return (
    <div className="flex h-10 items-center gap-2.5 rounded-lg px-2.5 py-1">
      <div className="size-8 shrink-0 animate-pulse rounded-md bg-muted" />
      <div className="min-w-0 flex-1 space-y-1.5">
        <div className="h-3.5 w-2/3 animate-pulse rounded bg-muted" />
        <div className="h-2.5 w-1/2 animate-pulse rounded bg-muted/70" />
      </div>
    </div>
  );
}

export function SearchDialog({
  open,
  onOpenChange,
}: {
  open: boolean;
  onOpenChange: (open: boolean) => void;
}) {
  const params = useParams<{ workspaceSlug?: string }>();
  const workspace = useWorkspaceStore((s) => s.currentWorkspace);
  const openPanel = usePanelStore((s) => s.open);

  const workspaceId =
    workspace?.id && isUuid(workspace.id) ? workspace.id : undefined;

  const [query, setQuery] = useState("");
  const debounced = useDebouncedValue(query, DEBOUNCE_MS);
  const inputRef = useRef<HTMLInputElement>(null);

  const searchEnabled = open && debounced.trim().length > 0;

  const searchQuery = api.search.search.useQuery(
    {
      query: debounced.trim(),
      workspaceId,
      limit: 20,
    },
    {
      enabled: searchEnabled,
      retry: false,
    },
  );

  const recentQuery = api.search.recent.useQuery(
    { workspaceId, limit: 10 },
    {
      enabled: open,
      retry: false,
    },
  );

  const useMockSearch = searchQuery.isError;
  const useMockRecent = recentQuery.isError;

  const resultRows = useMemo(() => {
    if (!searchEnabled) return [];
    if (useMockSearch) {
      const q = debounced.trim().toLowerCase();
      return MOCK_RESULTS.filter(
        (m) =>
          m.title.toLowerCase().includes(q) ||
          (m.descriptionSnippet?.toLowerCase().includes(q) ?? false),
      );
    }
    return searchQuery.data?.results ?? [];
  }, [
    searchEnabled,
    useMockSearch,
    debounced,
    searchQuery.data?.results,
  ]);

  const recentRows = useMemo(() => {
    if (useMockRecent) return MOCK_RECENT;
    return recentQuery.data?.results ?? [];
  }, [useMockRecent, recentQuery.data?.results]);

  const grouped = useMemo(() => groupResults(resultRows), [resultRows]);

  const flatList = useMemo(() => {
    const list: { group: string; label: string; item: SearchResultItem }[] = [];
    for (const g of grouped) {
      for (const item of g.items) {
        list.push({ group: g.key, label: g.label, item });
      }
    }
    return list;
  }, [grouped]);

  const emptyQuery = !query.trim();

  const showLoading =
    searchEnabled &&
    (searchQuery.isFetching || (searchQuery.isPending && !useMockSearch));

  const keyboardNavItems = useMemo(() => {
    if (showLoading) return [];
    if (searchEnabled && resultRows.length > 0) {
      return flatList.map((f) => f.item);
    }
    if (emptyQuery && recentRows.length > 0 && !recentQuery.isFetching) {
      return recentRows;
    }
    return [];
  }, [
    showLoading,
    searchEnabled,
    resultRows.length,
    flatList,
    emptyQuery,
    recentRows,
    recentQuery.isFetching,
  ]);

  const activeIndexById = useMemo(() => {
    const m = new Map<string, number>();
    keyboardNavItems.forEach((item, i) => m.set(item.id, i));
    return m;
  }, [keyboardNavItems]);

  const [active, setActive] = useState(0);

  useEffect(() => {
    setActive(0);
  }, [query, keyboardNavItems.length, searchEnabled, emptyQuery]);

  useEffect(() => {
    if (!open) {
      setQuery("");
      setActive(0);
    }
  }, [open]);

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

  const selectObject = useCallback(
    (obj: SearchResultItem) => {
      openPanel("object-detail", obj.id);
      onOpenChange(false);
      setQuery("");
    },
    [openPanel, onOpenChange],
  );

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

  const showRecentEmpty = emptyQuery && !showLoading;
  const showNoResults =
    searchEnabled && !showLoading && resultRows.length === 0;

  return (
    <DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
      <DialogPrimitive.Portal>
        <DialogPrimitive.Overlay
          className={cn(
            "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
          data-search-dialog-ignore-shortcut
          className={cn(
            "fixed left-1/2 top-[8vh] z-[201] w-[min(720px,calc(100vw-1.5rem))] -translate-x-1/2 rounded-xl border border-border bg-popover 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",
          )}
          onOpenAutoFocus={(ev) => ev.preventDefault()}
          onKeyDown={onKeyDown}
        >
          <div className="flex items-center gap-2 border-b border-border px-3 py-2.5">
            <Search className="size-5 shrink-0 text-muted-foreground" />
            <Input
              ref={inputRef}
              value={query}
              onChange={(e) => setQuery(e.target.value)}
              placeholder="Search tasks, docs, projects…"
              className="h-10 flex-1 border-0 bg-transparent px-0 text-base shadow-none placeholder:text-muted-foreground/80 focus-visible:ring-0"
              autoComplete="off"
              aria-label="Search workspace"
            />
            {query ? (
              <Button
                type="button"
                size="icon"
                variant="ghost"
                className="size-8 shrink-0 text-muted-foreground"
                onClick={() => setQuery("")}
                aria-label="Clear search"
              >
                <X className="size-4" />
              </Button>
            ) : null}
          </div>

          <ScrollArea className="max-h-[min(480px,65vh)]">
            <div className="px-2 pb-3 pt-1">
              {showLoading ? (
                <div className="space-y-1 px-1 pt-1">
                  {Array.from({ length: 6 }).map((_, i) => (
                    <ResultSkeleton key={i} />
                  ))}
                </div>
              ) : showRecentEmpty ? (
                <div className="px-1 pt-1">
                  <div className="px-2 pb-2 pt-1 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
                    {recentQuery.isFetching ? "Loading…" : "Recent"}
                  </div>
                  {recentQuery.isFetching && !useMockRecent ? (
                    <div className="space-y-1">
                      {Array.from({ length: 4 }).map((_, i) => (
                        <ResultSkeleton key={i} />
                      ))}
                    </div>
                  ) : (
                    <div className="space-y-0.5">
                      {recentRows.map((item) => (
                        <SearchResultRow
                          key={item.id}
                          object={item}
                          query=""
                          active={activeIndexById.get(item.id) === active}
                          onMouseEnter={() =>
                            setActive(activeIndexById.get(item.id) ?? 0)
                          }
                          onClick={() => selectObject(item)}
                        />
                      ))}
                    </div>
                  )}
                  <p className="px-2 pt-3 text-[11px] text-muted-foreground">
                    Tip: press{" "}
                    <kbd className="rounded border border-border bg-muted px-1 py-0.5 font-mono text-[10px]">
                      ⌘
                    </kbd>
                    <kbd className="ml-0.5 rounded border border-border bg-muted px-1 py-0.5 font-mono text-[10px]">
                      /
                    </kbd>{" "}
                    anytime to search.
                  </p>
                </div>
              ) : showNoResults ? (
                <div className="px-3 py-10 text-center">
                  <p className="text-sm font-medium text-foreground">
                    No results for{" "}
                    <span className="text-foreground">
                      {highlightText(debounced.trim(), debounced.trim())}
                    </span>
                  </p>
                  <p className="mt-2 text-sm text-muted-foreground">
                    Try a shorter keyword, check spelling, or search in another
                    workspace.
                  </p>
                  <ul className="mx-auto mt-4 max-w-sm list-inside list-disc text-left text-xs text-muted-foreground">
                    <li>Use words from the title or description</li>
                    <li>Remove filters in the sidebar if any</li>
                    <li>Browse recent items below when the query is empty</li>
                  </ul>
                </div>
              ) : (
                <div className="space-y-1 px-1 pt-1">
                  {grouped.map((g) => (
                    <div key={g.key}>
                      <div className="px-2 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground first:pt-0">
                        {g.label}
                      </div>
                      <div className="space-y-0.5">
                        {g.items.map((item) => {
                          const idx = activeIndexById.get(item.id) ?? -1;
                          return (
                            <SearchResultRow
                              key={item.id}
                              object={item}
                              query={debounced.trim()}
                              active={idx === active}
                              onMouseEnter={() =>
                                setActive(idx >= 0 ? idx : 0)
                              }
                              onClick={() => selectObject(item)}
                            />
                          );
                        })}
                      </div>
                    </div>
                  ))}
                </div>
              )}
            </div>
          </ScrollArea>

          <div className="flex flex-wrap items-center justify-between gap-2 border-t border-border px-3 py-2 text-[11px] text-muted-foreground">
            <span className="flex flex-wrap items-center gap-1.5">
              <CornerDownLeft className="size-3.5 opacity-70" />
              <kbd className="rounded border border-border bg-muted px-1.5 py-0.5 font-mono">
                ↑↓
              </kbd>
              navigate
              <span className="opacity-40">·</span>
              <kbd className="rounded border border-border bg-muted px-1.5 py-0.5 font-mono">
                ↵
              </kbd>
              open
            </span>
            <span className="opacity-80">
              {params?.workspaceSlug ? `/${params.workspaceSlug}` : ""}
            </span>
          </div>

          <DialogPrimitive.Title className="sr-only">Search workspace</DialogPrimitive.Title>
          <DialogPrimitive.Description className="sr-only">
            Find tasks, documents, projects, and whiteboards. Use arrow keys to
            navigate and Enter to open the detail panel.
          </DialogPrimitive.Description>
        </DialogPrimitive.Content>
      </DialogPrimitive.Portal>
    </DialogPrimitive.Root>
  );
}
