import { msg } from "@lingui/core/macro";
import { Trans, useLingui } from "@lingui/react/macro";
import {
  IconDeviceTv,
  IconHome,
  IconKeyboard,
  IconMovie,
  IconSearch,
  IconUser,
  IconX,
} from "@tabler/icons-react";
import { useHotkey, useHotkeySequence } from "@tanstack/react-hotkeys";
import { skipToken, useQuery } from "@tanstack/react-query";
import { useNavigate } from "@tanstack/react-router";
import { useAtom } from "jotai";
import { useCallback, useEffect, useRef, useState } from "react";

import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
  CommandShortcut,
} from "@/components/ui/command";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Kbd } from "@/components/ui/kbd";
import { Skeleton } from "@/components/ui/skeleton";
import { useDebounce } from "@/hooks/use-debounce";
import {
  commandPaletteOpenAtom,
  helpOpenAtom,
  MAX_RECENT,
  recentSearchesAtom,
} from "@/lib/atoms/command-palette";
import { orpc } from "@/lib/orpc/client";
import { i18n } from "@sofa/i18n";

// Static shortcut descriptions for the help dialog, pre-grouped by scope.
// TanStack's HotkeyManager/SequenceManager handle all actual key listening.
const SHORTCUT_GROUPS = [
  {
    scope: msg`Global`,
    shortcuts: [
      { description: msg`Search`, keys: ["/"] },
      { description: msg`Keyboard shortcuts`, keys: ["?"] },
    ],
  },
  {
    scope: msg`Navigation`,
    shortcuts: [
      { description: msg`Go to dashboard`, keys: ["g", "h"] },
      { description: msg`Go to explore`, keys: ["g", "e"] },
      { description: msg`Go to upcoming`, keys: ["g", "u"] },
      { description: msg`Go to settings`, keys: ["g", "s"] },
    ],
  },
  {
    scope: msg`Title`,
    shortcuts: [
      { description: msg`Cycle status`, keys: ["w"] },
      { description: msg`Mark watched`, keys: ["m"] },
      { description: msg`Go back`, keys: ["Escape"] },
      { description: msg`Rate 1 star`, keys: ["1"] },
      { description: msg`Rate 2 stars`, keys: ["2"] },
      { description: msg`Rate 3 stars`, keys: ["3"] },
      { description: msg`Rate 4 stars`, keys: ["4"] },
      { description: msg`Rate 5 stars`, keys: ["5"] },
    ],
  },
];

interface SearchResult {
  id?: string;
  tmdbId: number;
  type: "movie" | "tv" | "person";
  title: string;
  posterPath: string | null;
  profilePath: string | null;
  releaseDate: string | null;
  voteAverage: number | null;
  knownFor: string[] | null;
  knownForDepartment: string | null;
}

export function CommandPalette() {
  const { t } = useLingui();
  const navigate = useNavigate();
  const [commandPaletteOpen, setCommandPaletteOpen] = useAtom(commandPaletteOpenAtom);
  const [helpOpen, setHelpOpen] = useAtom(helpOpenAtom);
  const [recentSearches, setRecentSearches] = useAtom(recentSearchesAtom);
  const [query, setQuery] = useState("");
  const debouncedQuery = useDebounce(query, 300);
  const trimmedQuery = debouncedQuery.trim();
  const { data: searchData, isLoading: loading } = useQuery(
    orpc.discover.search.queryOptions({
      input: trimmedQuery ? { query: trimmedQuery } : skipToken,
    }),
  );
  const results: SearchResult[] = searchData?.results?.slice(0, 8) ?? [];
  const enabled = !commandPaletteOpen;

  const handleOpenChange = useCallback(
    (open: boolean | ((prev: boolean) => boolean)) => {
      setCommandPaletteOpen((prev) => {
        const next = typeof open === "function" ? open(prev) : open;
        if (next) setQuery("");
        return next;
      });
    },
    [setCommandPaletteOpen],
  );

  useHotkey("Mod+K", () => handleOpenChange((prev) => !prev));
  useHotkey("/", () => handleOpenChange(true), { enabled });
  useHotkey({ key: "?", shift: true }, () => setHelpOpen(true), { enabled });
  useHotkeySequence(
    ["G", "H"],
    () => {
      void navigate({ to: "/dashboard" });
    },
    { enabled, timeout: 500 },
  );
  useHotkeySequence(
    ["G", "E"],
    () => {
      void navigate({ to: "/explore" });
    },
    { enabled, timeout: 500 },
  );
  useHotkeySequence(
    ["G", "U"],
    () => {
      void navigate({ to: "/upcoming" });
    },
    { enabled, timeout: 500 },
  );
  useHotkeySequence(
    ["G", "S"],
    () => {
      void navigate({ to: "/settings" });
    },
    { enabled, timeout: 500 },
  );

  // Save to recent searches after user stops typing for a while
  const saveTimerRef = useRef<ReturnType<typeof setTimeout>>(null);
  useEffect(() => {
    if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
    const trimmed = debouncedQuery.trim();
    if (trimmed && results.length > 0) {
      saveTimerRef.current = setTimeout(() => {
        setRecentSearches((prev) => {
          const filtered = prev.filter((q) => q !== trimmed);
          return [trimmed, ...filtered].slice(0, MAX_RECENT);
        });
      }, 2000);
    }
    return () => {
      if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
    };
  }, [debouncedQuery, results.length, setRecentSearches]);

  const handleSelect = useCallback(
    (result: SearchResult) => {
      if (!result.id) return;
      setCommandPaletteOpen(false);
      if (result.type === "person") {
        void navigate({ to: "/people/$id", params: { id: result.id } });
      } else {
        void navigate({ to: "/titles/$id", params: { id: result.id } });
      }
    },
    [setCommandPaletteOpen, navigate],
  );

  const handleRecentSearch = useCallback((q: string) => {
    setQuery(q);
  }, []);

  const handleRemoveRecent = useCallback(
    (q: string) => {
      setRecentSearches((prev) => prev.filter((s) => s !== q));
    },
    [setRecentSearches],
  );

  const handleClearRecent = useCallback(() => {
    setRecentSearches([]);
  }, [setRecentSearches]);

  const hasQuery = query.trim().length > 0;

  return (
    <>
      <Dialog open={commandPaletteOpen} onOpenChange={handleOpenChange}>
        <DialogHeader className="sr-only">
          <DialogTitle>
            <Trans>Command Palette</Trans>
          </DialogTitle>
          <DialogDescription>
            <Trans>Search for movies, TV shows, or run commands</Trans>
          </DialogDescription>
        </DialogHeader>
        <DialogContent
          className="top-1/3 translate-y-0 overflow-hidden rounded-xl! p-0 sm:max-w-lg"
          showCloseButton={false}
        >
          <Command shouldFilter={false}>
            <CommandInput
              placeholder={t`Search movies & TV shows…`}
              value={query}
              onValueChange={setQuery}
            />
            <CommandList className="max-h-80">
              {hasQuery && loading && (
                <div className="space-y-2 p-3">
                  {Array.from({ length: 3 }).map((_, i) => (
                    <div key={`skel-${i}`} className="flex items-center gap-3">
                      <Skeleton className="h-12 w-8 shrink-0 rounded" />
                      <div className="flex-1 space-y-1.5">
                        <Skeleton className="h-3.5 w-3/4" />
                        <Skeleton className="h-3 w-1/3" />
                      </div>
                    </div>
                  ))}
                </div>
              )}

              {hasQuery && !loading && results.length === 0 && (
                <CommandEmpty>
                  <Trans>No results found.</Trans>
                </CommandEmpty>
              )}

              {hasQuery && !loading && results.length > 0 && (
                <CommandGroup heading={t`Results`}>
                  {results.map((r) => (
                    <CommandItem
                      key={r.id ?? `${r.type}-${r.tmdbId}`}
                      onSelect={() => handleSelect(r)}
                      className="flex items-center gap-3 py-2"
                    >
                      {r.type === "person" ? (
                        <div className="bg-muted size-10 shrink-0 overflow-hidden rounded-full">
                          {r.profilePath ? (
                            <img
                              src={r.profilePath}
                              alt={r.title}
                              width={40}
                              height={40}
                              loading="lazy"
                              decoding="async"
                              className="h-full w-full object-cover"
                            />
                          ) : (
                            <div className="flex h-full items-center justify-center">
                              <IconUser
                                aria-hidden={true}
                                className="text-muted-foreground size-4"
                              />
                            </div>
                          )}
                        </div>
                      ) : (
                        <div className="bg-muted h-12 w-8 shrink-0 overflow-hidden rounded">
                          {r.posterPath ? (
                            <img
                              src={r.posterPath}
                              alt={r.title}
                              width={32}
                              height={48}
                              loading="lazy"
                              decoding="async"
                              className="h-full w-full object-cover"
                            />
                          ) : (
                            <div className="text-muted-foreground flex h-full items-center justify-center text-[8px]">
                              ?
                            </div>
                          )}
                        </div>
                      )}
                      <div className="min-w-0 flex-1">
                        <p className="truncate text-xs font-medium">{r.title}</p>
                        <div className="text-muted-foreground flex items-center gap-1.5 text-[10px]">
                          {r.type === "person" ? (
                            <IconUser aria-hidden={true} className="size-[11px]" />
                          ) : r.type === "movie" ? (
                            <IconMovie aria-hidden={true} className="size-[11px]" />
                          ) : (
                            <IconDeviceTv aria-hidden={true} className="size-[11px]" />
                          )}
                          <span className="uppercase">
                            {r.type === "movie" ? t`movie` : r.type === "tv" ? t`tv` : t`person`}
                          </span>
                          {r.type !== "person" && r.releaseDate && (
                            <span>{r.releaseDate.slice(0, 4)}</span>
                          )}
                          {r.type === "person" && r.knownFor && r.knownFor.length > 0 && (
                            <span className="truncate">{r.knownFor.join(", ")}</span>
                          )}
                        </div>
                      </div>
                    </CommandItem>
                  ))}
                </CommandGroup>
              )}

              {!hasQuery && (
                <>
                  {recentSearches.length > 0 && (
                    <CommandGroup
                      heading={
                        <div className="flex items-center justify-between">
                          <span>
                            <Trans>Recent Searches</Trans>
                          </span>
                          <button
                            type="button"
                            onClick={handleClearRecent}
                            className="text-muted-foreground hover:text-foreground text-[10px] font-normal transition-colors"
                          >
                            <Trans>Clear all</Trans>
                          </button>
                        </div>
                      }
                    >
                      {recentSearches.map((q) => (
                        <CommandItem
                          key={q}
                          onSelect={() => handleRecentSearch(q)}
                          className="group"
                        >
                          <IconSearch
                            aria-hidden={true}
                            className="text-muted-foreground size-3.5"
                          />
                          <span className="flex-1">{q}</span>
                          <span data-slot="command-shortcut" className="ml-auto">
                            <button
                              type="button"
                              aria-label={t`Remove from recent searches`}
                              onClick={(e) => {
                                e.stopPropagation();
                                handleRemoveRecent(q);
                              }}
                              className="text-muted-foreground hover:text-foreground rounded-sm p-0.5 opacity-0 transition-opacity group-data-[selected=true]:opacity-100"
                            >
                              <IconX className="size-3" />
                            </button>
                          </span>
                        </CommandItem>
                      ))}
                    </CommandGroup>
                  )}
                  {recentSearches.length > 0 && <CommandSeparator />}
                  <CommandGroup heading={t`Quick Actions`}>
                    <CommandItem
                      onSelect={() => {
                        setCommandPaletteOpen(false);
                        void navigate({ to: "/dashboard" });
                      }}
                    >
                      <IconHome aria-hidden={true} className="size-3.5" />
                      <Trans>Go to Dashboard</Trans>
                      <CommandShortcut>G H</CommandShortcut>
                    </CommandItem>
                    <CommandItem
                      onSelect={() => {
                        setCommandPaletteOpen(false);
                        void navigate({ to: "/explore" });
                      }}
                    >
                      <IconSearch aria-hidden={true} className="size-3.5" />
                      <Trans>Go to Explore</Trans>
                      <CommandShortcut>G E</CommandShortcut>
                    </CommandItem>
                    <CommandItem
                      onSelect={() => {
                        setCommandPaletteOpen(false);
                        setHelpOpen(true);
                      }}
                    >
                      <IconKeyboard aria-hidden={true} className="size-3.5" />
                      <Trans>Keyboard Shortcuts</Trans>
                      <CommandShortcut>?</CommandShortcut>
                    </CommandItem>
                  </CommandGroup>
                </>
              )}
            </CommandList>
          </Command>
        </DialogContent>
      </Dialog>

      <Dialog open={helpOpen} onOpenChange={setHelpOpen}>
        <DialogContent className="sm:max-w-md">
          <DialogHeader>
            <DialogTitle>
              <Trans>Keyboard Shortcuts</Trans>
            </DialogTitle>
          </DialogHeader>
          <div className="space-y-5 py-2">
            {SHORTCUT_GROUPS.map((group) => (
              <div key={i18n._(group.scope)} className="space-y-2">
                <h3 className="text-muted-foreground text-[10px] font-semibold tracking-wider uppercase">
                  {i18n._(group.scope)}
                </h3>
                <div className="space-y-1">
                  {group.shortcuts.map((item) => (
                    <div
                      key={i18n._(item.description)}
                      className="flex items-center justify-between rounded-md px-2 py-1.5"
                    >
                      <span className="text-foreground text-xs">{i18n._(item.description)}</span>
                      <div className="flex items-center gap-1">
                        {item.keys.map((key, i) => (
                          <span key={key} className="flex items-center gap-1">
                            {i > 0 && (
                              <span className="text-muted-foreground text-[10px]">
                                <Trans>then</Trans>
                              </span>
                            )}
                            <Kbd>{formatKey(key)}</Kbd>
                          </span>
                        ))}
                      </div>
                    </div>
                  ))}
                </div>
              </div>
            ))}
          </div>
        </DialogContent>
      </Dialog>
    </>
  );
}

function formatKey(key: string): string {
  const map: Record<string, string> = {
    " ": "Space",
    Escape: "Esc",
    ArrowUp: "↑",
    ArrowDown: "↓",
    ArrowLeft: "←",
    ArrowRight: "→",
  };
  return map[key] ?? key.toUpperCase();
}
