import { useEffect, useId, useMemo, useRef, useState, type KeyboardEvent } from 'react'
import { AlertCircle, Check, ChevronDown } from 'lucide-react'

export interface SelectOption {
  value: string
  label: string
  disabled?: boolean
}

interface SelectProps {
  value: string
  onChange: (value: string) => void
  options: SelectOption[]
  placeholder?: string
  disabled?: boolean
  /** Показать ошибку (2px `--color-danger`, иконка `AlertCircle`) — текст ошибки рисует вызывающий код под полем. */
  error?: boolean
  id?: string
  'aria-label'?: string
  'aria-labelledby'?: string
}

/**
 * Кастомный селект (DESIGN_SYSTEM.md §4.16) — заменяет нативный `<select>`.
 * Паттерн listbox-combobox: триггер — геометрия инпута (§4.4), дроплист —
 * паттерн `.menu` (design/mockups/lobby.html), пункт — `.menu a`. Полностью
 * на var()-токенах — обе темы работают без отдельных правил (см. §4.16
 * «Обе темы»).
 *
 * Клавиатура: Enter/Space/стрелки открывают список (фокус остаётся на
 * триггере), стрелки двигают клавиатурную подсветку (не по кругу),
 * Home/End — к границам, ввод буквы — type-ahead, Enter/Space на подсвеченном
 * пункте — выбор и закрытие, Esc/клик вне/Tab — закрытие без изменений.
 */
export function Select({
  value,
  onChange,
  options,
  placeholder = 'Выберите…',
  disabled = false,
  error = false,
  id,
  ...aria
}: SelectProps) {
  const generatedId = useId()
  const baseId = id ?? generatedId
  const listboxId = `${baseId}-listbox`

  const rootRef = useRef<HTMLDivElement>(null)
  const triggerRef = useRef<HTMLButtonElement>(null)
  const optionRefs = useRef<Map<string, HTMLLIElement>>(new Map())
  const typeaheadRef = useRef<{ text: string; timer: ReturnType<typeof window.setTimeout> | undefined }>({
    text: '',
    timer: undefined,
  })

  const enabledOptions = useMemo(() => options.filter((o) => !o.disabled), [options])
  const selectedOption = options.find((o) => o.value === value) ?? null

  // `null` — единственный сентинел «нет активного пункта»; пустая строка
  // ('' — например, «По умолчанию» в SUMMARY_RECIPIENTS_OPTIONS) — легитимное
  // значение и НЕ должна схлопываться в `null` через `||`-фоллбэки (был баг:
  // ArrowDown ставил activeValue='', а `if (activeValue)`/`activeValue ? …`
  // ниже трактовали это как «ничего не подсвечено», из-за чего Enter не
  // выбирал пункт и aria-activedescendant не выставлялся). Поэтому везде ниже
  // сравнение только с `null` (`!== null`/`=== null`), никогда `||`/truthiness.
  const [open, setOpen] = useState(false)
  const [activeValue, setActiveValue] = useState<string | null>(
    enabledOptions.some((o) => o.value === value) ? value : null,
  )

  function closePanel() {
    setOpen(false)
  }

  function openPanel() {
    if (disabled) return
    const current = enabledOptions.find((o) => o.value === value)
    setActiveValue(current ? current.value : (enabledOptions[0]?.value ?? null))
    setOpen(true)
  }

  // Закрытие по клику вне триггера/панели — тот же приём, что бургер-меню ShellTopbar.
  useEffect(() => {
    if (!open) return
    function handlePointerDown(event: MouseEvent) {
      if (rootRef.current && !rootRef.current.contains(event.target as Node)) closePanel()
    }
    document.addEventListener('mousedown', handlePointerDown)
    return () => document.removeEventListener('mousedown', handlePointerDown)
  }, [open])

  useEffect(() => {
    if (open && activeValue !== null) {
      optionRefs.current.get(activeValue)?.scrollIntoView({ block: 'nearest' })
    }
  }, [open, activeValue])

  function moveActive(direction: 1 | -1) {
    if (enabledOptions.length === 0) return
    const currentIndex = enabledOptions.findIndex((o) => o.value === activeValue)
    const nextIndex =
      currentIndex === -1 ? 0 : Math.min(Math.max(currentIndex + direction, 0), enabledOptions.length - 1)
    setActiveValue(enabledOptions[nextIndex].value)
  }

  function commitActive() {
    if (activeValue !== null) onChange(activeValue)
    closePanel()
    triggerRef.current?.focus()
  }

  function selectOption(option: SelectOption) {
    if (option.disabled) return
    onChange(option.value)
    closePanel()
    triggerRef.current?.focus()
  }

  function handleTypeahead(char: string) {
    const buf = typeaheadRef.current
    window.clearTimeout(buf.timer)
    buf.text += char.toLowerCase()
    buf.timer = window.setTimeout(() => {
      buf.text = ''
    }, 600)
    const currentIndex = enabledOptions.findIndex((o) => o.value === activeValue)
    // Ищем от следующего после подсвеченного, по кругу — как в нативном select.
    const ordered = [...enabledOptions.slice(currentIndex + 1), ...enabledOptions.slice(0, currentIndex + 1)]
    const match = ordered.find((o) => o.label.toLowerCase().startsWith(buf.text))
    if (match) setActiveValue(match.value)
  }

  function handleTriggerKeyDown(event: KeyboardEvent<HTMLButtonElement>) {
    if (disabled) return

    if (!open) {
      if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown' || event.key === 'ArrowUp') {
        event.preventDefault()
        openPanel()
      }
      return
    }

    switch (event.key) {
      case 'ArrowDown':
        event.preventDefault()
        moveActive(1)
        break
      case 'ArrowUp':
        event.preventDefault()
        moveActive(-1)
        break
      case 'Home':
        event.preventDefault()
        if (enabledOptions[0]) setActiveValue(enabledOptions[0].value)
        break
      case 'End':
        event.preventDefault()
        if (enabledOptions.length > 0) setActiveValue(enabledOptions[enabledOptions.length - 1].value)
        break
      case 'Enter':
      case ' ':
        event.preventDefault()
        commitActive()
        break
      case 'Escape':
        event.preventDefault()
        closePanel()
        break
      case 'Tab':
        closePanel()
        break
      default:
        if (event.key.length === 1) handleTypeahead(event.key)
    }
  }

  return (
    <div className="vc-select" ref={rootRef}>
      <button
        type="button"
        ref={triggerRef}
        id={baseId}
        className={`vc-select-trigger${error ? ' has-error' : ''}`}
        role="combobox"
        aria-haspopup="listbox"
        aria-expanded={open}
        aria-controls={listboxId}
        aria-activedescendant={open && activeValue !== null ? `${listboxId}-opt-${activeValue}` : undefined}
        aria-disabled={disabled || undefined}
        disabled={disabled}
        onClick={() => (open ? closePanel() : openPanel())}
        onKeyDown={handleTriggerKeyDown}
        {...aria}
      >
        {error && <AlertCircle className="vc-select-error-icon" style={{ width: 16, height: 16 }} aria-hidden="true" />}
        <span className={selectedOption ? 'vc-select-value' : 'vc-select-placeholder'}>
          {selectedOption ? selectedOption.label : placeholder}
        </span>
        <ChevronDown className={`vc-select-chevron${open ? ' is-open' : ''}`} style={{ width: 18, height: 18 }} aria-hidden="true" />
      </button>

      {open && (
        <ul className="vc-select-panel" role="listbox" id={listboxId}>
          {options.map((option) => {
            const isSelected = option.value === value
            const isActive = option.value === activeValue
            return (
              <li
                key={option.value}
                id={`${listboxId}-opt-${option.value}`}
                ref={(el) => {
                  if (el) optionRefs.current.set(option.value, el)
                  else optionRefs.current.delete(option.value)
                }}
                role="option"
                aria-selected={isSelected}
                aria-disabled={option.disabled || undefined}
                className={`vc-select-option${isActive ? ' is-active' : ''}${isSelected ? ' is-selected' : ''}${option.disabled ? ' is-disabled' : ''}`}
                onMouseEnter={() => !option.disabled && setActiveValue(option.value)}
                onClick={() => selectOption(option)}
              >
                <span>{option.label}</span>
                {isSelected && <Check style={{ width: 16, height: 16 }} aria-hidden="true" />}
              </li>
            )
          })}
        </ul>
      )}
    </div>
  )
}
