import { useEffect, useMemo, useRef, useState } from 'react'
import { Check, ChevronDown } from 'lucide-react'
import styles from './MultiSelectDropdown.module.css'

export interface MultiSelectOption<T extends string | number = number> {
  value: T
  label: string
  depth?: number
}

interface MultiSelectDropdownProps<T extends string | number = number> {
  options: MultiSelectOption<T>[]
  value: T[]
  onChange: (value: T[]) => void
  placeholder?: string
  searchPlaceholder?: string
  disabled?: boolean
  id?: string
}

export function MultiSelectDropdown<T extends string | number = number>({
  options,
  value,
  onChange,
  placeholder = 'Select options',
  searchPlaceholder = 'Search...',
  disabled = false,
  id,
}: MultiSelectDropdownProps<T>) {
  const [open, setOpen] = useState(false)
  const containerRef = useRef<HTMLDivElement>(null)
  const [searchQuery, setSearchQuery] = useState('')
  const searchInputRef = useRef<HTMLInputElement>(null)

  const selectedLabels = useMemo(
    () =>
      options
        .filter((option) => value.includes(option.value))
        .map((option) => option.label),
    [options, value],
  )

  const filteredOptions = useMemo(() => {
    const query = searchQuery.trim().toLowerCase()
    if (!query) return options
    return options.filter((o) => o.label.toLowerCase().includes(query))
  }, [options, searchQuery])

  const triggerLabel = useMemo(() => {
    if (selectedLabels.length === 0) return placeholder
    if (selectedLabels.length <= 2) return selectedLabels.join(', ')
    return `${selectedLabels.slice(0, 2).join(', ')} +${selectedLabels.length - 2}`
  }, [placeholder, selectedLabels])

  useEffect(() => {
    if (!open) return
    setSearchQuery('')

    const t = window.setTimeout(() => {
      searchInputRef.current?.focus()
    }, 0)

    function handleClickOutside(e: MouseEvent) {
      if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
        setOpen(false)
      }
    }

    function handleEscape(e: KeyboardEvent) {
      if (e.key === 'Escape') setOpen(false)
    }

    document.addEventListener('mousedown', handleClickOutside)
    document.addEventListener('keydown', handleEscape)
    return () => {
      window.clearTimeout(t)
      document.removeEventListener('mousedown', handleClickOutside)
      document.removeEventListener('keydown', handleEscape)
    }
  }, [open])

  function toggleOption(optionValue: T) {
    if (value.includes(optionValue)) {
      onChange(value.filter((item) => item !== optionValue))
      return
    }
    onChange([...value, optionValue])
  }

  return (
    <div className={styles.wrapper} ref={containerRef}>
      <button
        id={id}
        type="button"
        className={`${styles.trigger} ${open ? styles.triggerOpen : ''} ${
          selectedLabels.length === 0 ? styles.triggerPlaceholder : ''
        }`}
        onClick={() => !disabled && setOpen((prev) => !prev)}
        disabled={disabled}
        aria-haspopup="listbox"
        aria-expanded={open}
      >
        <span className={styles.triggerText}>{triggerLabel}</span>
        <ChevronDown size={16} className={`${styles.chevron} ${open ? styles.chevronOpen : ''}`} />
      </button>

      {open && (
        <ul className={styles.dropdown} role="listbox" aria-multiselectable="true">
          <li className={styles.searchRow}>
            <input
              ref={searchInputRef}
              className={styles.searchInput}
              value={searchQuery}
              onChange={(e) => setSearchQuery(e.target.value)}
              placeholder={searchPlaceholder}
              aria-label="Search options"
              disabled={disabled}
            />
          </li>

          {filteredOptions.length === 0 ? (
            <li className={styles.empty}>{options.length === 0 ? 'No options available.' : 'No matching options.'}</li>
          ) : (
            filteredOptions.map((option) => {
              const selected = value.includes(option.value)
              return (
                <li key={String(option.value)}>
                  <button
                    type="button"
                    role="option"
                    aria-selected={selected}
                    className={`${styles.option} ${selected ? styles.optionSelected : ''}`}
                    style={{ paddingLeft: `${10 + (option.depth ?? 0) * 18}px` }}
                    onClick={() => toggleOption(option.value)}
                  >
                    <span className={`${styles.check} ${selected ? styles.checkSelected : ''}`}>
                      {selected && <Check size={12} strokeWidth={3} />}
                    </span>
                    <span>{option.label}</span>
                  </button>
                </li>
              )
            })
          )}
        </ul>
      )}
    </div>
  )
}
