"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import type { DragEvent as ReactDragEvent } from "react";
import { useRouter, useSelectedLayoutSegments } from "next/navigation";
import { CalendarClock, ChevronsUpDown, Copy, Layers, Search } from "lucide-react";
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card";
import { cn } from "@/lib/utils";
import { useSearchPaletteStore, type ReferenceInsertMode, type SearchPaletteMode } from "@/store/search-palette";
import type { DocumentSearchResult, DocumentSearchRequest, DocumentSearchTimeRange } from "@/types/search";
import { useDocumentSearch } from "@/hooks/use-document-search";
import { useReferenceComposer } from "@/hooks/use-reference-composer";
import { recordRecentPage } from "@/lib/search/record-recent";
import { PageHoverCard } from "@/components/reference/page-hover-card";
import { buildSearchRequest } from "@/lib/search/request";
import { resolveSearchOpenMode } from "@/lib/search/shortcuts";

interface SearchPaletteProps {
  workspaceId: string | null;
}

const TIME_RANGE_LABEL: Record<DocumentSearchTimeRange, string> = {
  any: "全部时间",
  "7d": "最近 7 天",
  "30d": "最近 30 天",
};

const formatDate = (value: string | null) => {
  if (!value) return "未知时间";
  const date = new Date(value);
  return date.toLocaleString();
};

export function SearchPalette({ workspaceId }: SearchPaletteProps) {
  const router = useRouter();
  const segments = useSelectedLayoutSegments();
  const activeDocumentId = segments?.[1] ?? null;
  const inputRef = useRef<HTMLInputElement>(null);
  const {
    open,
    mode,
    query,
    filters,
    timeRange,
    referenceMode,
    alias,
    recent,
    setRecent,
    openSearch,
    openReference,
    close,
    setQuery,
    toggleFilter,
    setTimeRange,
    setTimeField,
    setCustomRange,
    setReferenceMode,
    setAlias,
    rememberResult,
  } = useSearchPaletteStore();
  const [highlightedIndex, setHighlightedIndex] = useState(0);
  const { insertReference } = useReferenceComposer({
    workspaceId,
    sourcePageId: activeDocumentId ?? null,
  });
  const [resultTab, setResultTab] = useState<"all" | "recent">(recent.length > 0 ? "recent" : "all");
  const [referenceFiltersOpen, setReferenceFiltersOpen] = useState(mode === "reference");
  const updateCustomRange = useCallback(
    (patch: { from?: string; to?: string }) => {
      const next = { ...(filters.customRange ?? {}), ...patch };
      if (!next.from && !next.to) {
        setCustomRange(null);
      } else {
        setCustomRange(next);
      }
    },
    [filters.customRange, setCustomRange],
  );
  const clearCustomRange = useCallback(() => setCustomRange(null), [setCustomRange]);

  const requestPayload = useMemo<DocumentSearchRequest | null>(
    () =>
      buildSearchRequest({
        workspaceId,
        activeDocumentId,
        query,
        filters,
        timeRange,
      }),
    [workspaceId, activeDocumentId, query, filters, timeRange],
  );

  const searchQueryEnabled = Boolean(open && workspaceId);
  const { data, isLoading, isFetching, error } = useDocumentSearch(requestPayload, searchQueryEnabled);

  useEffect(() => {
    if (data?.recent) {
      setRecent(data.recent);
    }
  }, [data?.recent, setRecent]);
  /* eslint-disable react-hooks/set-state-in-effect */
  useEffect(() => {
    if (mode === "reference") {
      setReferenceFiltersOpen(true);
    } else {
      setReferenceFiltersOpen(false);
    }
  }, [mode]);
  /* eslint-enable react-hooks/set-state-in-effect */
  const searchResults = data?.results;
  const remoteResults = useMemo(() => searchResults ?? [], [searchResults]);
  const showRecent = !query.trim() && recent.length > 0;
  const enableRecentTab = showRecent;
  const effectiveTab = enableRecentTab ? resultTab : "all";
  const activeResults = effectiveTab === "recent" ? recent : remoteResults;

  useEffect(() => {
    if (!open) {
      return undefined;
    }
    inputRef.current?.focus();
    const frame = requestAnimationFrame(() => setHighlightedIndex(0));
    return () => cancelAnimationFrame(frame);
  }, [open]);

  useEffect(() => {
    if (highlightedIndex >= activeResults.length) {
      const frame = requestAnimationFrame(() =>
        setHighlightedIndex(activeResults.length > 0 ? activeResults.length - 1 : 0),
      );
      return () => cancelAnimationFrame(frame);
    }
    return undefined;
  }, [activeResults.length, highlightedIndex]);

  useEffect(() => {
    const handleGlobalHotkey = (event: KeyboardEvent) => {
      if ((event.metaKey || event.ctrlKey) && !event.shiftKey && event.key.toLowerCase() === "p") {
        event.preventDefault();
        openSearch();
      }
      if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.key.toLowerCase() === "r") {
        event.preventDefault();
        openReference();
      }
    };
    window.addEventListener("keydown", handleGlobalHotkey);
    return () => window.removeEventListener("keydown", handleGlobalHotkey);
  }, [openSearch, openReference]);

  const handleOpenResult = useCallback(
    (result: DocumentSearchResult, openMode: "main" | "new-window" | "sidebar") => {
      if (openMode === "new-window") {
        window.open(result.publicPath, "_blank", "noopener,noreferrer");
      } else if (openMode === "sidebar") {
        window.open(`${result.publicPath}?preview=sidebar`, "_blank", "noopener,noreferrer");
      } else {
        router.push(result.publicPath);
      }
      void recordRecentPage(workspaceId, result.id);
      rememberResult(result);
      close();
    },
    [router, workspaceId, rememberResult, close],
  );

  const handleInsertReference = useCallback(
    (result: DocumentSearchResult, overrideMode?: ReferenceInsertMode) => {
      const run = async () => {
        try {
          const effectiveMode = overrideMode ?? referenceMode;
          await insertReference(result, {
            mode: effectiveMode,
            alias: effectiveMode === "inline" ? alias : undefined,
          });
          rememberResult(result);
          close();
        } catch (error) {
          console.error(error);
        }
      };
      void run();
    },
    [alias, close, insertReference, referenceMode, rememberResult],
  );

  const handleCopyReference = useCallback((result: DocumentSearchResult) => {
    if (typeof window === "undefined") {
      return;
    }
    const payload = `((${result.id}))`;
    if (navigator?.clipboard) {
      navigator.clipboard
        .writeText(payload)
        .then(() => window.alert("块引用已复制"))
        .catch(() => {
          window.prompt("复制失败，请手动复制引用内容", payload);
        });
    } else {
      window.prompt("复制块引用", payload);
    }
  }, []);

  useEffect(() => {
    if (!open) {
      return;
    }
    const handleKey = (event: KeyboardEvent) => {
      if (event.key === "ArrowDown") {
        event.preventDefault();
        setHighlightedIndex((prev) => Math.min(activeResults.length - 1, prev + 1));
      } else if (event.key === "ArrowUp") {
        event.preventDefault();
        setHighlightedIndex((prev) => Math.max(0, prev - 1));
      } else if (event.key === "Enter") {
        event.preventDefault();
        if (activeResults.length === 0) {
          return;
        }
        const result = activeResults[highlightedIndex] ?? activeResults[0];
        if (!result) return;
        if (mode === "search") {
          const openMode = resolveSearchOpenMode(event);
          handleOpenResult(result, openMode);
        } else {
          handleInsertReference(result);
        }
      } else if (event.key === "Escape") {
        event.preventDefault();
        close();
      }
    };
    window.addEventListener("keydown", handleKey);
    return () => window.removeEventListener("keydown", handleKey);
  }, [open, activeResults, highlightedIndex, mode, handleOpenResult, handleInsertReference, close]);

  const pending = isLoading || isFetching;
  const dialogTitle = mode === "search" ? "页面搜索面板" : "引用选择面板";

  return (
    <Dialog open={open} onOpenChange={(next) => !next && close()}>
      <DialogContent className="max-h-[90vh] w-full max-w-3xl overflow-hidden border-none bg-white/95 p-0 shadow-xl">
        <DialogTitle className="sr-only">{dialogTitle}</DialogTitle>
        <div className="flex h-[520px] flex-col">
          <div className="border-b border-[#eef2ff] p-4">
            <div className="flex items-center gap-3">
              <div className="relative flex-1">
                <Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400" />
                <Input
                  ref={inputRef}
                  value={query}
                  onChange={(event) => {
                    const nextValue = event.target.value;
                    if (nextValue.trim().length > 0 && resultTab !== "all") {
                      setResultTab("all");
                    }
                    setQuery(nextValue);
                  }}
                  placeholder={mode === "search" ? "搜索页面标题、正文或 OCR 内容..." : "选择要引用的页面"}
                  className="h-11 w-full rounded-xl border border-[#e2e8f0] bg-white pl-9 text-sm"
                />
              </div>
              <DropdownMenu>
                <DropdownMenuTrigger asChild>
                  <Button variant="ghost" className="gap-2 text-xs text-gray-600">
                    <CalendarClock className="h-4 w-4" />
                    {TIME_RANGE_LABEL[timeRange]}
                  </Button>
                </DropdownMenuTrigger>
                <DropdownMenuContent align="end" className="w-40">
                  {(Object.keys(TIME_RANGE_LABEL) as DocumentSearchTimeRange[]).map((range) => (
                    <DropdownMenuItem
                      key={range}
                      onClick={() => setTimeRange(range)}
                      className={cn(range === timeRange && "bg-[#eef2ff] text-[#2563eb]")}
                    >
                      {TIME_RANGE_LABEL[range]}
                    </DropdownMenuItem>
                  ))}
                </DropdownMenuContent>
              </DropdownMenu>
              <DropdownMenu>
                <DropdownMenuTrigger asChild>
                  <Button variant="ghost" className="text-xs text-gray-600">
                    {filters.timeField === "updated" ? "按编辑时间" : "按创建时间"}
                  </Button>
                </DropdownMenuTrigger>
                <DropdownMenuContent align="end" className="w-32">
                  <DropdownMenuItem
                    onClick={() => setTimeField("updated")}
                    className={cn(filters.timeField === "updated" && "bg-[#eef2ff] text-[#2563eb]")}
                  >
                    按编辑时间
                  </DropdownMenuItem>
                  <DropdownMenuItem
                    onClick={() => setTimeField("created")}
                    className={cn(filters.timeField === "created" && "bg-[#eef2ff] text-[#2563eb]")}
                  >
                    按创建时间
                  </DropdownMenuItem>
                </DropdownMenuContent>
              </DropdownMenu>
            </div>
            <div className="mt-3 flex flex-wrap items-center gap-2 text-xs">
              <FilterToggle label="仅标题" active={filters.titleOnly} onClick={() => toggleFilter("titleOnly")} />
              <FilterToggle label="精确匹配" active={filters.exact} onClick={() => toggleFilter("exact")} />
              <FilterToggle
                label="当前页面"
                active={filters.onlyCurrentPage && Boolean(activeDocumentId)}
                disabled={!activeDocumentId}
                onClick={() => toggleFilter("onlyCurrentPage")}
              />
              <FilterToggle
                label="图片 OCR"
                active={filters.includeOcr}
                onClick={() => toggleFilter("includeOcr")}
              />
              {mode === "reference" && (
                <Button
                  type="button"
                  size="sm"
                  variant="ghost"
                  className="ml-auto flex items-center gap-1 rounded-full text-xs text-gray-600"
                  onClick={() => setReferenceFiltersOpen((prev) => !prev)}
                >
                  <ChevronsUpDown className="h-3 w-3" />
                  {referenceFiltersOpen ? "隐藏引用筛选" : "展开引用筛选"}
                </Button>
              )}
            </div>
            {mode === "reference" && referenceFiltersOpen && (
              <div className="mt-2 flex flex-wrap items-center gap-2 text-xs text-gray-500">
                <ReferenceModeToggle
                  label="行内引用"
                  active={referenceMode === "inline"}
                  onClick={() => setReferenceMode("inline")}
                />
                <ReferenceModeToggle
                  label="嵌入块"
                  active={referenceMode === "embed"}
                  onClick={() => setReferenceMode("embed")}
                />
                {referenceMode === "inline" && (
                  <Input
                    value={alias}
                    onChange={(event) => setAlias(event.target.value)}
                    placeholder="引用别名"
                    className="h-8 w-40 text-xs"
                  />
                )}
                <span className="text-[11px] text-gray-400">引用模式将同步到 [[ / # 快捷键</span>
              </div>
            )}
            <div className="mt-2 flex flex-wrap items-center gap-2 text-xs text-gray-500">
              <span>自定义时间</span>
              <Input
                type="date"
                value={filters.customRange?.from ?? ""}
                onChange={(event) => updateCustomRange({ from: event.target.value || undefined })}
                className="h-8 w-36 text-xs"
              />
              <Input
                type="date"
                value={filters.customRange?.to ?? ""}
                onChange={(event) => updateCustomRange({ to: event.target.value || undefined })}
                className="h-8 w-36 text-xs"
              />
              <Button
                type="button"
                variant="ghost"
                size="sm"
                className="text-xs text-gray-500"
                onClick={clearCustomRange}
                disabled={!filters.customRange?.from && !filters.customRange?.to}
              >
                清除
              </Button>
            </div>
          </div>
          <div className="flex-1 overflow-hidden">
            {enableRecentTab && (
              <div className="flex items-center gap-2 border-b border-[#eef2ff] px-4 py-2 text-xs">
                <TabButton
                  label="最近访问"
                  active={resultTab === "recent"}
                  onClick={() => setResultTab("recent")}
                  disabled={!enableRecentTab}
                />
                <TabButton label="全部页面" active={resultTab === "all"} onClick={() => setResultTab("all")} />
              </div>
            )}
            {pending ? (
              <div className="flex h-full items-center justify-center text-sm text-gray-500">
                检索中，请稍候...
              </div>
            ) : error ? (
              <div className="flex h-full items-center justify-center text-sm text-red-500">
                {(error as Error).message}
              </div>
            ) : activeResults.length === 0 ? (
              <div className="flex h-full items-center justify-center text-sm text-gray-400">
                {effectiveTab === "recent" ? "暂无最近访问记录" : "暂无匹配结果"}
              </div>
            ) : (
              <div className="h-full divide-y divide-[#f1f5f9] overflow-y-auto">
                {activeResults.map((result, index) => (
                  <ResultRow
                    key={`result-${result.id}-${index}-${resultTab}`}
                    result={result}
                    highlighted={highlightedIndex === index}
                    mode={mode}
                    onOpen={() => handleOpenResult(result, "main")}
                    onReference={() => handleInsertReference(result)}
                    onEmbed={() => handleInsertReference(result, "embed")}
                    onCopy={() => handleCopyReference(result)}
                  />
                ))}
              </div>
            )}
          </div>
          <div className="border-t border-[#eef2ff] px-4 py-2 text-xs text-gray-500">
            {mode === "search" ? (
              <div className="flex items-center justify-between">
                <span>Enter 打开 · Ctrl/Cmd+Enter 新窗口 · Alt+Enter 右侧预览</span>
                <span>Esc 关闭</span>
              </div>
            ) : (
              <div className="flex items-center justify-between">
                <span>
                  {referenceMode === "inline" ? "插入行内引用（链接高亮）" : "插入一个嵌入的页面块"}，可先输入别名
                </span>
                <span>Esc 关闭</span>
              </div>
            )}
          </div>
        </div>
      </DialogContent>
    </Dialog>
  );
}

function ResultRow({
  result,
  highlighted,
  mode,
  onOpen,
  onReference,
  onEmbed,
  onCopy,
}: {
  result: DocumentSearchResult;
  highlighted: boolean;
  mode: SearchPaletteMode;
  onOpen: () => void;
  onReference: () => void;
  onEmbed: () => void;
  onCopy: () => void;
}) {
  const handleActivate = () => {
    if (mode === "search") {
      onOpen();
    } else {
      onReference();
    }
  };

  const handleDragStart = (event: ReactDragEvent<HTMLDivElement>) => {
    if (mode !== "reference") return;
    if (event.dataTransfer) {
      event.dataTransfer.setData("text/plain", `((${result.id}))`);
      event.dataTransfer.effectAllowed = "copy";
    }
  };

  const content = (
    <div
      role="button"
      tabIndex={0}
      onClick={handleActivate}
      onKeyDown={(event) => {
        if (event.key === "Enter") {
          event.preventDefault();
          handleActivate();
        }
      }}
      className={cn(
        "cursor-pointer px-4 py-3 transition-colors hover:bg-[#eef2ff]",
        highlighted && "bg-[#e3ecff]",
      )}
      draggable={mode === "reference"}
      onDragStart={handleDragStart}
    >
      <div className="flex items-center gap-2 text-sm font-medium text-gray-900">
        {result.title || "无标题"}
        {result.matchField !== "recent" && (
          <Badge variant="secondary" className="bg-[#edf2ff] text-xs text-[#2563eb]">
            {result.matchField === "title" ? "标题匹配" : "正文匹配"}
          </Badge>
        )}
        {result.hasOcr && (
          <Badge variant="outline" className="text-[10px] text-[#475569]">
            OCR
          </Badge>
        )}
      </div>
      <div className="mt-1 text-xs text-gray-500" dangerouslySetInnerHTML={{ __html: result.snippet }} />
      <div className="mt-2 text-[11px] text-gray-400">
        最近编辑：{formatDate(result.updatedAt)} · 创建时间：{formatDate(result.createdAt)}
      </div>
      {mode === "reference" && (
        <div className="mt-3 flex flex-wrap items-center gap-2 text-[11px] text-gray-500">
          <Button
            type="button"
            size="sm"
            variant="ghost"
            className="h-7 rounded-full px-3"
            onClick={(event) => {
              event.preventDefault();
              event.stopPropagation();
              onReference();
            }}
          >
            行内引用
          </Button>
          <Button
            type="button"
            size="sm"
            variant="ghost"
            className="flex h-7 items-center gap-1 rounded-full px-3"
            onClick={(event) => {
              event.preventDefault();
              event.stopPropagation();
              onEmbed();
            }}
          >
            <Layers className="h-3 w-3" />
            嵌入到...
          </Button>
          <Button
            type="button"
            size="sm"
            variant="ghost"
            className="flex h-7 items-center gap-1 rounded-full px-3"
            onClick={(event) => {
              event.preventDefault();
              event.stopPropagation();
              onCopy();
            }}
          >
            <Copy className="h-3 w-3" />
            复制块引用
          </Button>
          <span className="ml-auto text-[10px] text-gray-400">也可直接拖拽引用到编辑器</span>
        </div>
      )}
    </div>
  );

  return (
    <HoverCard openDelay={250}>
      <HoverCardTrigger asChild>{content}</HoverCardTrigger>
      <HoverCardContent align="start">
        <PageHoverCard result={result} onPreview={onOpen} />
      </HoverCardContent>
    </HoverCard>
  );
}

interface FilterToggleProps {
  label: string;
  active: boolean;
  onClick: () => void;
  disabled?: boolean;
}

function FilterToggle({ label, active, onClick, disabled }: FilterToggleProps) {
  return (
    <Button
      type="button"
      size="sm"
      variant={active ? "default" : "ghost"}
      disabled={disabled}
      className={cn(
        "h-7 rounded-full border px-3 text-xs",
        active
          ? "border-[#2563eb] bg-[#2563eb] text-white"
          : "border-transparent text-gray-500 hover:bg-gray-100",
      )}
      onClick={onClick}
    >
      {label}
    </Button>
  );
}

interface TabButtonProps {
  label: string;
  active: boolean;
  onClick: () => void;
  disabled?: boolean;
}

function TabButton({ label, active, onClick, disabled }: TabButtonProps) {
  return (
    <Button
      type="button"
      size="sm"
      variant={active ? "default" : "ghost"}
      disabled={disabled}
      className={cn(
        "rounded-full px-4 text-xs",
        active ? "bg-[#2563eb] text-white" : "text-gray-600 hover:bg-[#eef2ff]",
      )}
      onClick={onClick}
    >
      {label}
    </Button>
  );
}

interface ReferenceModeToggleProps {
  label: string;
  active: boolean;
  onClick: () => void;
}

function ReferenceModeToggle({ label, active, onClick }: ReferenceModeToggleProps) {
  return (
    <Button
      type="button"
      size="sm"
      variant={active ? "default" : "ghost"}
      className={cn(
        "h-7 rounded-full border px-3 text-[11px]",
        active
          ? "border-[#2563eb] bg-[#2563eb] text-white"
          : "border-transparent text-gray-500 hover:bg-gray-100",
      )}
      onClick={onClick}
    >
      {label}
    </Button>
  );
}
