import { useEffect, useMemo, useRef, useState } from 'react';
import { ChevronDown, Search, Check } from 'lucide-react';
import { cn } from '@/lib/cn';

export interface ComboboxOption {
  value: string;
  label: string;
  hint?: string;
}

/**
 * Searchable single-select dropdown. Replaces native <select> when the option
 * list is long or needs filtering. Keyboard: ↑/↓ to move, Enter to choose, Esc to close.
 */
export function Combobox({
  value,
  options,
  onChange,
  placeholder = 'Select…',
  searchPlaceholder = 'Search…',
  className,
  disabled,
}: {
  value: string;
  options: ComboboxOption[];
  onChange: (v: string) => void;
  placeholder?: string;
  searchPlaceholder?: string;
  className?: string;
  disabled?: boolean;
}) {
  const [open, setOpen]       = useState(false);
  const [query, setQuery]     = useState('');
  const [highlight, setHi]    = useState(0);
  const rootRef     = useRef<HTMLDivElement>(null);
  const inputRef    = useRef<HTMLInputElement>(null);
  const listRef     = useRef<HTMLUListElement>(null);

  const selected = options.find((o) => o.value === value);

  const filtered = useMemo(() => {
    if (!query.trim()) return options;
    const q = query.toLowerCase();
    return options.filter((o) =>
      o.label.toLowerCase().includes(q) || o.value.toLowerCase().includes(q),
    );
  }, [options, query]);

  // Focus search when opened
  useEffect(() => {
    if (open) {
      setQuery('');
      const sel = filtered.findIndex((o) => o.value === value);
      setHi(sel >= 0 ? sel : 0);
      // delay focus so the panel mounts first
      setTimeout(() => inputRef.current?.focus(), 0);
    }
  }, [open]); // eslint-disable-line react-hooks/exhaustive-deps

  // Close on outside click
  useEffect(() => {
    if (!open) return;
    const onDown = (e: MouseEvent) => {
      if (rootRef.current && !rootRef.current.contains(e.target as Node)) {
        setOpen(false);
      }
    };
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [open]);

  // Keep highlighted option in view
  useEffect(() => {
    if (!open || !listRef.current) return;
    const li = listRef.current.children[highlight] as HTMLElement | undefined;
    li?.scrollIntoView({ block: 'nearest' });
  }, [highlight, open]);

  const handleKey = (e: React.KeyboardEvent) => {
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      setHi((h) => Math.min(filtered.length - 1, h + 1));
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      setHi((h) => Math.max(0, h - 1));
    } else if (e.key === 'Enter') {
      e.preventDefault();
      const opt = filtered[highlight];
      if (opt) {
        onChange(opt.value);
        setOpen(false);
      }
    } else if (e.key === 'Escape') {
      setOpen(false);
    }
  };

  return (
    <div ref={rootRef} className={cn('relative', className)}>
      <button
        type="button"
        disabled={disabled}
        onClick={() => setOpen((o) => !o)}
        className={cn(
          'w-full !justify-between flex items-center gap-2 h-9 px-3 rounded-lg border bg-white text-sm text-left transition-colors',
          'focus-visible:ring-2 focus-visible:ring-brand-400 focus-visible:border-brand-500',
          open ? 'border-brand-500 ring-2 ring-brand-200' : 'border-ink-200 hover:border-ink-300',
          disabled && 'opacity-50 cursor-not-allowed',
        )}
      >
        <span className={cn('truncate', selected ? 'text-ink-900' : 'text-ink-400')}>
          {selected ? selected.label : placeholder}
        </span>
        <ChevronDown className={cn('h-4 w-4 text-ink-500 shrink-0 transition-transform', open && 'rotate-180')} />
      </button>

      {open && (
        <div className="absolute z-50 mt-1.5 w-full rounded-xl border border-ink-200 bg-white shadow-pop overflow-hidden animate-[backreload-slide-in_120ms_ease-out]">
          {/* Search */}
          <div className="relative border-b border-ink-100">
            <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-ink-400" />
            <input
              ref={inputRef}
              type="text"
              value={query}
              onChange={(e) => { setQuery(e.target.value); setHi(0); }}
              onKeyDown={handleKey}
              placeholder={searchPlaceholder}
              className="!border-0 !ring-0 w-full h-10 pl-9 pr-3 text-sm bg-transparent focus:outline-none"
            />
          </div>

          {/* Options */}
          {filtered.length === 0 ? (
            <div className="px-3 py-6 text-xs text-ink-500 text-center">No matches.</div>
          ) : (
            <ul ref={listRef} className="max-h-60 overflow-auto py-1" role="listbox">
              {filtered.map((o, i) => {
                const isSel = o.value === value;
                const isHi  = i === highlight;
                return (
                  <li key={o.value} role="option" aria-selected={isSel}>
                    <button
                      type="button"
                      onMouseEnter={() => setHi(i)}
                      onClick={() => { onChange(o.value); setOpen(false); }}
                      className={cn(
                        'w-full !justify-start flex items-center gap-2 px-3 h-9 text-sm text-left',
                        isHi ? 'bg-brand-50 text-ink-900' : 'text-ink-800',
                      )}
                    >
                      <Check className={cn('h-3.5 w-3.5 shrink-0', isSel ? 'text-brand-600' : 'opacity-0')} />
                      <span className="flex-1 truncate">{o.label}</span>
                      {o.hint && <span className="text-[11px] text-ink-500 shrink-0">{o.hint}</span>}
                    </button>
                  </li>
                );
              })}
            </ul>
          )}
        </div>
      )}
    </div>
  );
}
