import { useState, useMemo, useRef, useEffect } from 'react';
import { Dropdown } from './dropdown';
import { Input } from './input';
import { Button } from './button';
import { cn } from '@/lib/utils';
import { ChevronDown, X, Check } from 'lucide-react';

/**
 * SelectOption - Option d'un composant Select
 *
 * @interface SelectOption
 */
export interface SelectOption {
  /**
   * Valeur de l'option
   */
  value: string;

  /**
   * Label affiché de l'option
   */
  label: string;

  /**
   * Si `true`, l'option est désactivée
   */
  disabled?: boolean;

  /**
   * Nom du groupe auquel appartient l'option (optionnel)
   */
  group?: string;
}

/**
 * SelectGroup - Groupe d'options pour un Select
 *
 * @interface SelectGroup
 */
export interface SelectGroup {
  /**
   * Label du groupe
   */
  label: string;

  /**
   * Options du groupe
   */
  options: SelectOption[];
}

/**
 * SelectProps - Propriétés du composant Select
 *
 * @interface SelectProps
 */
export interface SelectProps {
  /**
   * Tableau d'options disponibles
   */
  options: SelectOption[];

  /**
   * Valeur(s) sélectionnée(s)
   * String pour single select, array pour multi-select
   */
  value?: string | string[];

  /**
   * Fonction appelée lorsque la sélection change
   *
   * @param {string | string[]} value - Nouvelle(s) valeur(s) sélectionnée(s)
   */
  onChange: (value: string | string[]) => void;

  /**
   * Si `true`, permet la sélection multiple
   *
   * @default false
   */
  multiple?: boolean;

  /**
   * Si `true`, active la recherche dans les options
   *
   * @default false
   */
  searchable?: boolean;

  /**
   * Texte du placeholder
   *
   * @default 'Select an option...'
   */
  placeholder?: string;

  /**
   * Si `true`, désactive le select
   *
   * @default false
   */
  disabled?: boolean;

  /**
   * Classes CSS personnalisées
   */
  className?: string;

  /**
   * Attribut name pour les formulaires
   */
  name?: string;

  /**
   * CRITIQUE FIX #42: Label accessible pour le select (aria-label)
   */
  'aria-label'?: string;

  /**
   * CRITIQUE FIX #42: ID d'un élément qui décrit le select (aria-labelledby)
   */
  'aria-labelledby'?: string;
}

/**
 * Select - Composant de sélection avec recherche, multi-select et groupes
 *
 * Composant de sélection avancé avec support pour :
 * - Sélection simple ou multiple
 * - Recherche dans les options
 * - Groupes d'options
 * - Validation et états désactivés
 *
 * @example
 * ```tsx
 * // Select simple
 * <Select
 *   options={[
 *     { value: '1', label: 'Option 1' },
 *     { value: '2', label: 'Option 2' }
 *   ]}
 *   value={selected}
 *   onChange={setSelected}
 * />
 * ```
 *
 * @example
 * ```tsx
 * // Multi-select avec recherche et groupes
 * <Select
 *   options={options}
 *   value={selectedValues}
 *   onChange={setSelectedValues}
 *   multiple
 *   searchable
 *   placeholder="Sélectionner..."
 * />
 * ```
 *
 * @component
 * @param {SelectProps} props - Propriétés du composant
 * @returns {JSX.Element} Select avec dropdown et recherche
 */
export function Select({
  options,
  value,
  onChange,
  multiple = false,
  searchable = false,
  placeholder = 'Select an option...',
  disabled = false,
  className,
  name,
  'aria-label': ariaLabel,
  'aria-labelledby': ariaLabelledBy,
}: SelectProps) {
  const [open, setOpen] = useState(false);
  const [search, setSearch] = useState('');
  const searchInputRef = useRef<HTMLInputElement>(null);

  // Grouper les options par groupe
  const groupedOptions = useMemo(() => {
    const groups: Record<string, SelectOption[]> = {};
    const ungrouped: SelectOption[] = [];

    options.forEach((option) => {
      if (option.group) {
        if (!groups[option.group]) {
          groups[option.group] = [];
        }
        groups[option.group].push(option);
      } else {
        ungrouped.push(option);
      }
    });

    return { groups, ungrouped };
  }, [options]);

  // Filtrer les options selon la recherche
  const filteredOptions = useMemo(() => {
    if (!searchable || !search) {
      return {
        groups: groupedOptions.groups,
        ungrouped: groupedOptions.ungrouped,
      };
    }

    const searchLower = search.toLowerCase();
    const filteredGroups: Record<string, SelectOption[]> = {};
    const filteredUngrouped: SelectOption[] = [];

    Object.entries(groupedOptions.groups).forEach(
      ([groupLabel, groupOptions]) => {
        const filtered = groupOptions.filter((opt) =>
          opt.label.toLowerCase().includes(searchLower),
        );
        if (filtered.length > 0) {
          filteredGroups[groupLabel] = filtered;
        }
      },
    );

    filteredUngrouped.push(
      ...groupedOptions.ungrouped.filter((opt) =>
        opt.label.toLowerCase().includes(searchLower),
      ),
    );

    return { groups: filteredGroups, ungrouped: filteredUngrouped };
  }, [searchable, search, groupedOptions]);

  // Obtenir les labels des valeurs sélectionnées
  const getSelectedLabels = () => {
    if (!value) return [];
    const values = Array.isArray(value) ? value : [value];
    return values
      .map((v) => options.find((opt) => opt.value === v)?.label)
      .filter(Boolean) as string[];
  };

  const selectedLabels = getSelectedLabels();
  const displayValue = multiple
    ? selectedLabels.length > 0
      ? `${selectedLabels.length} selected`
      : placeholder
    : selectedLabels[0] || placeholder;

  const isSelected = (optionValue: string) => {
    if (!value) return false;
    if (multiple) {
      return Array.isArray(value) && value.includes(optionValue);
    }
    return value === optionValue;
  };

  const handleSelect = (optionValue: string) => {
    if (multiple) {
      const currentValues = Array.isArray(value) ? value : [];
      const newValues = currentValues.includes(optionValue)
        ? currentValues.filter((v) => v !== optionValue)
        : [...currentValues, optionValue];
      onChange(newValues);
    } else {
      onChange(optionValue);
      setOpen(false);
      setSearch('');
    }
  };

  const handleClear = (e: React.MouseEvent) => {
    e.stopPropagation();
    onChange(multiple ? [] : '');
  };

  // Focus sur le champ de recherche quand le dropdown s'ouvre
  useEffect(() => {
    if (open && searchable && searchInputRef.current) {
      searchInputRef.current.focus();
    }
  }, [open, searchable]);

  // Réinitialiser la recherche quand le dropdown se ferme
  useEffect(() => {
    if (!open) {
      setSearch('');
    }
  }, [open]);

  const trigger = (
    <Button
      variant="outline"
      disabled={disabled}
      className={cn(
        'w-full justify-between',
        !value || (Array.isArray(value) && value.length === 0)
          ? 'text-muted-foreground'
          : '',
        className,
      )}
      type="button"
      // CRITIQUE FIX #42: Ajouter aria-label et aria-labelledby pour l'accessibilité
      aria-label={ariaLabel}
      aria-labelledby={ariaLabelledBy}
      aria-haspopup="listbox"
      aria-expanded={open}
    >
      <span className="truncate">{displayValue}</span>
      <div className="flex items-center gap-1 ml-2">
        {value &&
          ((Array.isArray(value) && value.length > 0) ||
            !Array.isArray(value)) && (
            <X
              className="h-4 w-4 shrink-0 opacity-50 hover:opacity-100"
              onClick={handleClear}
            />
          )}
        <ChevronDown className="h-4 w-4 shrink-0 opacity-50" />
      </div>
    </Button>
  );

  const dropdownContent = (
    <div
      className="w-full min-w-[200px] max-h-[300px] overflow-y-auto"
      role="listbox" // CRITIQUE FIX #54: Ajouter role="listbox" pour le conteneur des options
      aria-label={ariaLabel || name || placeholder}
    >
      {/* Champ de recherche */}
      {searchable && (
        <div className="p-2 border-b">
          <Input
            ref={searchInputRef}
            type="text"
            placeholder="Search..."
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            onClick={(e) => e.stopPropagation()}
            className="w-full"
          />
        </div>
      )}

      {/* Options non groupées */}
      {filteredOptions.ungrouped.length > 0 && (
        <div className="py-1">
          {filteredOptions.ungrouped.map((option) => (
            <SelectOptionItem
              key={option.value}
              option={option}
              isSelected={isSelected(option.value)}
              multiple={multiple}
              onSelect={handleSelect}
            />
          ))}
        </div>
      )}

      {/* Options groupées */}
      {Object.entries(filteredOptions.groups).map(
        ([groupLabel, groupOptions]) => (
          <div key={groupLabel} className="py-1">
            <div className="px-3 py-1.5 text-xs font-semibold text-gray-400 uppercase">
              {groupLabel}
            </div>
            {groupOptions.map((option) => (
              <SelectOptionItem
                key={option.value}
                option={option}
                isSelected={isSelected(option.value)}
                multiple={multiple}
                onSelect={handleSelect}
              />
            ))}
          </div>
        ),
      )}

      {/* Message si aucune option */}
      {filteredOptions.ungrouped.length === 0 &&
        Object.keys(filteredOptions.groups).length === 0 && (
          <div className="px-3 py-2 text-sm text-gray-400 text-center">
            No options found
          </div>
        )}
    </div>
  );

  return (
    <>
      <Dropdown
        trigger={trigger}
        align="left"
        onOpenChange={setOpen}
        className="w-full"
      >
        {dropdownContent}
      </Dropdown>
      {/* Input caché pour les formulaires */}
      {name && (
        <input
          type="hidden"
          name={name}
          value={Array.isArray(value) ? value.join(',') : value || ''}
        />
      )}
    </>
  );
}

interface SelectOptionItemProps {
  option: SelectOption;
  isSelected: boolean;
  multiple: boolean;
  onSelect: (value: string) => void;
}

function SelectOptionItem({
  option,
  isSelected,
  multiple,
  onSelect,
}: SelectOptionItemProps) {
  // CRITIQUE FIX #49: Gestion du clavier pour les options
  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (option.disabled) return;

    // Enter ou Space pour sélectionner
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      onSelect(option.value);
    }
    // Les flèches sont gérées par le composant Select parent
  };

  return (
    <div
      role="option"
      aria-selected={isSelected}
      className={cn(
        'relative flex items-center px-3 py-2 text-sm cursor-pointer',
        'hover:bg-white/5 hover:text-white',
        'focus:bg-white/5 focus:text-white',
        'transition-colors text-gray-300',
        isSelected && 'bg-kodo-cyan/10 text-kodo-cyan',
        option.disabled && 'opacity-50 cursor-not-allowed pointer-events-none',
      )}
      onClick={() => !option.disabled && onSelect(option.value)}
      onKeyDown={handleKeyDown}
      tabIndex={option.disabled ? -1 : 0}
    >
      {multiple && (
        <div
          className={cn(
            'mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-kodo-steel',
            isSelected && 'bg-kodo-cyan border-kodo-cyan text-kodo-void',
          )}
        >
          {isSelected && <Check className="h-3 w-3" />}
        </div>
      )}
      <span className="flex-1">{option.label}</span>
      {!multiple && isSelected && <Check className="h-4 w-4 text-kodo-cyan" />}
    </div>
  );
}
