import './ContextCombobox.css';
import { useEffect, useId, useMemo, useRef, useState } from 'react';
import { FiCheck, FiChevronDown, FiSearch } from 'react-icons/fi';
import type { InwinkContextOption } from '../context/InwinkContextProvider';

interface ContextComboboxProps {
  label: string;
  placeholder: string;
  value?: string;
  options: InwinkContextOption[];
  isLoading: boolean;
  error: string | null;
  onChange: (id: string) => void;
  /**
   * Si fourni, la recherche est déléguée au serveur (le terme saisi est transmis)
   * et la liste n'est PAS refiltrée côté client. Sans ce prop, le filtrage se fait
   * côté client sur le libellé.
   */
  onSearch?: (query: string) => void;
}

/**
 * Sélecteur ergonomique réutilisable : bouton déclencheur + popover avec champ de
 * recherche, liste filtrable et navigation clavier. Mutualise l'UI des pickers de
 * contexte (Event / Community / Audience).
 */
export default function ContextCombobox({
  label,
  placeholder,
  value,
  options,
  isLoading,
  error,
  onChange,
  onSearch,
}: ContextComboboxProps) {
  const [open, setOpen] = useState(false);
  const [query, setQuery] = useState('');
  const [highlight, setHighlight] = useState(0);
  const containerRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLInputElement>(null);
  const listId = useId();

  const selected = options.find((option) => option.id === value);
  const triggerLabel = selected?.label ?? (value && isLoading ? 'Chargement…' : placeholder);

  // En mode serveur (`onSearch`), la liste est déjà filtrée par l'API : on ne refiltre
  // pas côté client (une recherche par id n'apparaît pas dans le libellé).
  const filtered = useMemo(() => {
    if (onSearch) return options;
    const q = query.trim().toLowerCase();
    return q ? options.filter((option) => option.label.toLowerCase().includes(q)) : options;
  }, [options, query, onSearch]);

  // Fermeture au clic extérieur.
  useEffect(() => {
    if (!open) return;
    const onPointerDown = (event: MouseEvent) => {
      if (!containerRef.current?.contains(event.target as Node)) {
        setOpen(false);
      }
    };
    document.addEventListener('mousedown', onPointerDown);
    return () => document.removeEventListener('mousedown', onPointerDown);
  }, [open]);

  // Focus du champ de recherche à l'ouverture, et réinitialisation de la liste
  // (en mode serveur, on recharge la liste complète non filtrée).
  useEffect(() => {
    if (open) {
      setQuery('');
      setHighlight(0);
      inputRef.current?.focus();
      onSearch?.('');
    }
  }, [open, onSearch]);

  const select = (id: string) => {
    onChange(id);
    setOpen(false);
  };

  const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
    if (event.key === 'ArrowDown') {
      event.preventDefault();
      setHighlight((h) => Math.min(h + 1, filtered.length - 1));
    } else if (event.key === 'ArrowUp') {
      event.preventDefault();
      setHighlight((h) => Math.max(h - 1, 0));
    } else if (event.key === 'Enter') {
      event.preventDefault();
      const option = filtered[highlight];
      if (option) select(option.id);
    } else if (event.key === 'Escape') {
      setOpen(false);
    }
  };

  return (
    <div className="context-combobox" ref={containerRef}>
      <span className="context-combobox__label">{label}</span>
      <button
        type="button"
        className="context-combobox__trigger"
        disabled={Boolean(error)}
        aria-haspopup="listbox"
        aria-expanded={open}
        title={error ?? undefined}
        onClick={() => setOpen((o) => !o)}
      >
        <span className="context-combobox__value">
          {error ? 'Erreur de chargement' : triggerLabel}
        </span>
        <FiChevronDown aria-hidden className="context-combobox__chevron" />
      </button>

      {open && (
        <div className="context-combobox__panel">
          <div className="context-combobox__search">
            <FiSearch aria-hidden />
            <input
              ref={inputRef}
              type="text"
              value={query}
              placeholder="Rechercher…"
              aria-label={`Rechercher ${label.toLowerCase()}`}
              onChange={(e) => {
                setQuery(e.target.value);
                setHighlight(0);
                onSearch?.(e.target.value);
              }}
              onKeyDown={onInputKeyDown}
            />
          </div>
          <ul className="context-combobox__list" role="listbox" id={listId}>
            {isLoading && <li className="context-combobox__empty">Chargement…</li>}
            {!isLoading && filtered.length === 0 && (
              <li className="context-combobox__empty">Aucun résultat</li>
            )}
            {!isLoading &&
              filtered.map((option, index) => {
                const isSelected = option.id === value;
                const isHighlighted = index === highlight;
                return (
                  <li key={option.id} role="option" aria-selected={isSelected}>
                    <button
                      type="button"
                      className={
                        'context-combobox__option' +
                        (isHighlighted ? ' is-highlighted' : '') +
                        (isSelected ? ' is-selected' : '')
                      }
                      onMouseEnter={() => setHighlight(index)}
                      onClick={() => select(option.id)}
                    >
                      <span>{option.label}</span>
                      {isSelected && <FiCheck aria-hidden />}
                    </button>
                  </li>
                );
              })}
          </ul>
        </div>
      )}
    </div>
  );
}
