import { useState, useRef, useEffect } from 'react'
import { createPortal } from 'react-dom'
import styles from './MultiSelect.module.css'

export interface MultiSelectOption {
  value: string
  label: string
}

interface MultiSelectProps {
  options: MultiSelectOption[]
  value: string[]
  onChange: (value: string[]) => void
  placeholder?: string
  searchable?: boolean
  disabled?: boolean
  className?: string
}

export function MultiSelect({
  options,
  value,
  onChange,
  placeholder = 'Select...',
  searchable = true,
  disabled = false,
  className,
}: MultiSelectProps) {
  const [open, setOpen] = useState(false)
  const [search, setSearch] = useState('')
  const [pending, setPending] = useState<string[]>(value)
  const triggerRef = useRef<HTMLButtonElement>(null)
  const panelRef = useRef<HTMLDivElement>(null)
  const [pos, setPos] = useState({ top: 0, left: 0, width: 0 })

  // Sync pending with value when opening
  useEffect(() => {
    if (open) {
      setPending(value)
      setSearch('')
    }
  }, [open, value])

  // Position the panel below the trigger
  useEffect(() => {
    if (open && triggerRef.current) {
      const rect = triggerRef.current.getBoundingClientRect()
      setPos({
        top: rect.bottom + 4,
        left: rect.left,
        width: rect.width,
      })
    }
  }, [open])

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

  // Close on Escape
  useEffect(() => {
    if (!open) return
    function handleKey(e: KeyboardEvent) {
      if (e.key === 'Escape') setOpen(false)
    }
    document.addEventListener('keydown', handleKey)
    return () => document.removeEventListener('keydown', handleKey)
  }, [open])

  function toggleOption(optValue: string) {
    setPending((prev) =>
      prev.includes(optValue) ? prev.filter((v) => v !== optValue) : [...prev, optValue]
    )
  }

  function handleApply() {
    onChange(pending)
    setOpen(false)
  }

  const filtered = options.filter((opt) =>
    opt.label.toLowerCase().includes(search.toLowerCase())
  )

  const triggerLabel = value.length > 0 ? `${value.length} selected` : placeholder

  return (
    <div className={`${styles.wrap} ${className ?? ''}`}>
      <button
        ref={triggerRef}
        className={styles.trigger}
        onClick={() => !disabled && setOpen(!open)}
        role="combobox"
        aria-expanded={open}
        aria-haspopup="listbox"
        aria-disabled={disabled}
        type="button"
      >
        <span className={value.length > 0 ? styles.triggerText : styles.triggerPlaceholder}>
          {triggerLabel}
        </span>
        <span className={styles.chevron} aria-hidden="true">▾</span>
      </button>

      {open && createPortal(
        <div
          ref={panelRef}
          className={styles.panel}
          style={{ top: pos.top, left: pos.left, width: Math.max(pos.width, 200) }}
        >
          {searchable && (
            <input
              className={styles.search}
              placeholder="Search..."
              value={search}
              onChange={(e) => setSearch(e.target.value)}
              autoFocus
            />
          )}
          <div className={styles.optionList} role="listbox">
            {filtered.map((opt) => (
              <label key={opt.value} className={styles.option} role="option" aria-selected={pending.includes(opt.value)}>
                <input
                  type="checkbox"
                  className={styles.checkbox}
                  checked={pending.includes(opt.value)}
                  onChange={() => toggleOption(opt.value)}
                  aria-label={opt.label}
                />
                <span className={styles.optionLabel}>{opt.label}</span>
              </label>
            ))}
            {filtered.length === 0 && (
              <div className={styles.empty}>No matches</div>
            )}
          </div>
          <div className={styles.footer}>
            <button
              className={styles.applyBtn}
              onClick={handleApply}
              type="button"
            >
              Apply ({pending.length})
            </button>
          </div>
        </div>,
        document.body,
      )}
    </div>
  )
}
