import { Fragment, useEffect, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import type { LucideIcon } from 'lucide-react';
import {
  CommandDialog,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
  CommandShortcut,
} from '@/components/ui/command';

export interface CommandDestination {
  to: string;
  label: string;
  icon: LucideIcon;
  shortcut?: string;
  group?: string;
}

export function CommandPalette({
  destinations,
  open,
  onOpenChange,
}: {
  destinations: readonly CommandDestination[];
  open: boolean;
  onOpenChange: (open: boolean) => void;
}) {
  const navigate = useNavigate();
  const [query, setQuery] = useState('');
  const groups = Array.from(new Set(destinations.map((destination) => destination.group ?? 'Navigate')));

  // Clear on close rather than on open: reopening must not present yesterday's
  // query over a list it is already silently filtering. Keyed off `open` and
  // not the close handler because ⌘K and a selected item both close the dialog
  // by setting the prop directly.
  useEffect(() => {
    if (!open) setQuery('');
  }, [open]);

  // Where focus came from, so it can go back there. This used to be
  // `onCloseAutoFocus: preventDefault` — necessary while the header's search
  // control was an input that opened the palette on focus, because restoring
  // focus reopened the dialog. That control is a button now, and suppressing
  // restoration left focus on `<body>`: Tab then restarted at the top of the
  // document, which is a keyboard trap of its own. Radix's own restoration
  // does not survive this dialog either (measured: focus lands on `<body>`),
  // so the opener is captured and refocused explicitly.
  const opener = useRef<HTMLElement | null>(null);
  useEffect(() => {
    if (open) opener.current = document.activeElement as HTMLElement | null;
  }, [open]);

  return (
    <CommandDialog
      open={open}
      onOpenChange={onOpenChange}
      contentProps={{
        onCloseAutoFocus: (event) => {
          const target = opener.current;
          // `isConnected` because selecting an item navigates, and the opener
          // may be a control the new route has already unmounted; falling back
          // to Radix's default is better than focusing a detached node.
          if (!target || !target.isConnected) return;
          event.preventDefault();
          target.focus();
        },
      }}
    >
      <CommandInput
        value={query}
        onValueChange={setQuery}
        placeholder="Search pages and workflows…"
        aria-label="Search pages and workflows"
      />
      <CommandList className="max-h-[min(70dvh,32rem)] p-1">
        <CommandEmpty>No pages found.</CommandEmpty>
        {groups.map((group, index) => (
          <Fragment key={group}>
            {index > 0 ? <CommandSeparator /> : null}
            <CommandGroup heading={group}>
              {destinations
                .filter((destination) => (destination.group ?? 'Navigate') === group)
                .map((destination) => (
                  <CommandItem
                    key={destination.to}
                    value={`${destination.label} ${group}`}
                    className="min-h-11 rounded-lg"
                    onSelect={() => {
                      navigate(destination.to);
                      onOpenChange(false);
                    }}
                  >
                    <destination.icon aria-hidden />
                    <span>{destination.label}</span>
                    {destination.shortcut ? (
                      <CommandShortcut>{destination.shortcut}</CommandShortcut>
                    ) : null}
                  </CommandItem>
                ))}
            </CommandGroup>
          </Fragment>
        ))}
      </CommandList>
    </CommandDialog>
  );
}
