import { useEffect, useMemo, useRef, useState } from 'react';
import { cn } from '../lib/cn';

export interface CmdCommand {
  label: string;
  group?: string;
  kbd?: string;
  onSelect: () => void;
}

export interface CmdPaletteProps {
  open: boolean;
  onClose: () => void;
  commands: CmdCommand[];
  placeholder?: string;
  className?: string;
}

export function CmdPalette({
  open,
  onClose,
  commands,
  placeholder = 'Type a command or search...',
  className,
}: CmdPaletteProps) {
  const [query, setQuery] = useState('');
  const [active, setActive] = useState(0);
  const inputRef = useRef<HTMLInputElement>(null);

  const filtered = useMemo(() => {
    const q = query.trim().toLowerCase();
    if (!q) return commands;
    return commands.filter((c) => c.label.toLowerCase().includes(q) || c.group?.toLowerCase().includes(q));
  }, [query, commands]);

  const grouped = useMemo(() => {
    const map = new Map<string, CmdCommand[]>();
    filtered.forEach((c) => {
      const k = c.group || 'Commands';
      if (!map.has(k)) map.set(k, []);
      map.get(k)!.push(c);
    });
    return Array.from(map.entries());
  }, [filtered]);

  useEffect(() => {
    if (!open) return;
    setQuery('');
    setActive(0);
    setTimeout(() => inputRef.current?.focus(), 0);
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose();
    };
    document.addEventListener('keydown', onKey);
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      document.removeEventListener('keydown', onKey);
      document.body.style.overflow = prev;
    };
  }, [open, onClose]);

  useEffect(() => {
    setActive(0);
  }, [query]);

  if (!open) return null;

  const onKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      setActive((a) => Math.min(a + 1, filtered.length - 1));
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      setActive((a) => Math.max(a - 1, 0));
    } else if (e.key === 'Enter') {
      e.preventDefault();
      const cmd = filtered[active];
      if (cmd) {
        cmd.onSelect();
        onClose();
      }
    }
  };

  let runningIndex = -1;

  return (
    <div className="fixed inset-0 z-50 flex items-start justify-center p-4 pt-[15vh]" onClick={onClose}>
      <div className="absolute inset-0 bg-text/40 backdrop-blur-sm" />
      <div
        role="dialog"
        aria-modal="true"
        onClick={(e) => e.stopPropagation()}
        className={cn(
          'relative w-full max-w-xl bg-surface border border-border rounded-2xl shadow-2xl overflow-hidden',
          className
        )}
      >
        <div className="flex items-center gap-3 px-4 py-3 border-b border-border">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="text-text-3">
            <circle cx="11" cy="11" r="7" />
            <line x1="21" y1="21" x2="16.65" y2="16.65" />
          </svg>
          <input
            ref={inputRef}
            type="text"
            role="combobox"
            aria-expanded="true"
            aria-controls="cmd-listbox"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            onKeyDown={onKeyDown}
            placeholder={placeholder}
            className="flex-1 bg-transparent text-text placeholder:text-text-3 outline-none text-sm"
          />
          <kbd className="text-xs font-mono px-1.5 py-0.5 rounded bg-surface-2 text-text-3 border border-border">ESC</kbd>
        </div>

        <ul id="cmd-listbox" role="listbox" className="max-h-80 overflow-y-auto py-2">
          {grouped.length === 0 && (
            <li className="px-4 py-8 text-center text-sm text-text-3">No results.</li>
          )}
          {grouped.map(([group, items]) => (
            <li key={group}>
              <div className="px-4 pt-3 pb-1 text-xs font-medium uppercase tracking-wider text-text-3">{group}</div>
              <ul>
                {items.map((cmd) => {
                  runningIndex += 1;
                  const i = runningIndex;
                  return (
                    <li
                      key={`${group}-${cmd.label}`}
                      role="option"
                      aria-selected={i === active}
                      onMouseEnter={() => setActive(i)}
                      onClick={() => {
                        cmd.onSelect();
                        onClose();
                      }}
                      className={cn(
                        'flex items-center justify-between mx-2 px-3 py-2 rounded-md text-sm cursor-pointer',
                        i === active ? 'bg-primary-soft text-primary-deep' : 'text-text-2 hover:bg-surface-2'
                      )}
                    >
                      <span className="truncate">{cmd.label}</span>
                      {cmd.kbd && (
                        <kbd className="text-xs font-mono px-1.5 py-0.5 rounded bg-surface-2 text-text-3 border border-border ml-2">
                          {cmd.kbd}
                        </kbd>
                      )}
                    </li>
                  );
                })}
              </ul>
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
}
