import { useState, useRef, useEffect, useCallback, useId } from 'react'
import { createPortal } from 'react-dom'
import { IconChevronDownFilled } from '@tabler/icons-react'

interface Option {
  value: string | number
  label: string
}

interface Props {
  value: string | number
  options: Option[]
  onChange: (value: string | number) => void
  className?: string
  style?: React.CSSProperties
}

export default function Select({ value, options, onChange, className, style }: Props) {
  const [open, setOpen] = useState(false)
  const triggerRef = useRef<HTMLButtonElement>(null)
  const dropRef = useRef<HTMLDivElement>(null)
  const optionRefs = useRef<(HTMLButtonElement | null)[]>([])
  const [pos, setPos] = useState({ top: 0, left: 0, width: 0 })
  const listboxId = useId()

  const selected = options.find(o => String(o.value) === String(value))
  const selectedIndex = options.findIndex(o => String(o.value) === String(value))

  const close = useCallback(() => setOpen(false), [])

  const updatePos = useCallback(() => {
    if (!triggerRef.current) return
    const rect = triggerRef.current.getBoundingClientRect()
    setPos({ top: rect.bottom, left: rect.left, width: rect.width })
  }, [])

  // move real focus to the option matching the current value (or the first)
  useEffect(() => {
    if (!open) return
    updatePos()
    const target = optionRefs.current[selectedIndex >= 0 ? selectedIndex : 0]
    target?.focus()
  }, [open, selectedIndex, updatePos])

  useEffect(() => {
    if (!open) return
    function onDown(e: MouseEvent) {
      const target = e.target
      if (target instanceof Node && dropRef.current && !dropRef.current.contains(target) &&
          triggerRef.current && !triggerRef.current.contains(target)) {
        setOpen(false)
      }
    }
    window.addEventListener('scroll', updatePos, true)
    document.addEventListener('mousedown', onDown)
    return () => {
      window.removeEventListener('scroll', updatePos, true)
      document.removeEventListener('mousedown', onDown)
    }
  }, [open, updatePos])

  const moveFocus = useCallback((delta: number) => {
    const refs = optionRefs.current
    const current = refs.findIndex(el => el === document.activeElement)
    const next = Math.min(refs.length - 1, Math.max(0, (current >= 0 ? current : 0) + delta))
    refs[next]?.focus()
  }, [])

  // trigger-level keys: arrows cycle values like a native select while closed
  const onTriggerKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
      e.preventDefault()
      const delta = e.key === 'ArrowDown' ? 1 : -1
      const next = Math.min(options.length - 1, Math.max(0, selectedIndex + delta))
      if (next !== selectedIndex && options[next]) onChange(options[next].value)
      return
    }
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault()
      setOpen(!open)
      return
    }
    if (e.key === 'Escape') {
      close()
    }
  }

  const onOptionKeyDown = (e: React.KeyboardEvent, index: number) => {
    if (e.key === 'ArrowDown') { e.preventDefault(); moveFocus(1); return }
    if (e.key === 'ArrowUp') { e.preventDefault(); moveFocus(-1); return }
    if (e.key === 'Home') { e.preventDefault(); optionRefs.current[0]?.focus(); return }
    if (e.key === 'End') { e.preventDefault(); optionRefs.current[options.length - 1]?.focus(); return }
    if (e.key === 'Escape') { e.preventDefault(); close(); triggerRef.current?.focus(); return }
    if (e.key === 'Tab') { close(); return }
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault()
      const o = options[index]
      if (o) { onChange(o.value); close(); triggerRef.current?.focus() }
    }
  }

  return (
    <div className={`cv-select ${className || ''}`} style={style}>
      <button className="cv-select-trigger" ref={triggerRef} onClick={() => setOpen(!open)} type="button"
        aria-haspopup="listbox" aria-expanded={open} aria-controls={open ? listboxId : undefined}
        onKeyDown={onTriggerKeyDown}>
        <span>{selected?.label || String(value)}</span>
        <IconChevronDownFilled size={10} style={{ transform: open ? 'rotate(180deg)' : 'none', transition: 'transform 150ms' }} />
      </button>
      {open && createPortal(
        <div className="cv-select-dropdown" ref={dropRef} id={listboxId} role="listbox"
          style={{ top: pos.top, left: pos.left, minWidth: pos.width }}>
          {options.map((o, i) => (
            <button
              key={String(o.value)}
              ref={el => { optionRefs.current[i] = el }}
              role="option"
              aria-selected={String(o.value) === String(value)}
              tabIndex={i === (selectedIndex >= 0 ? selectedIndex : 0) ? 0 : -1}
              className={`cv-select-option ${String(o.value) === String(value) ? 'cv-select-option-active' : ''}`}
              onClick={() => { onChange(o.value); close() }}
              onKeyDown={e => onOptionKeyDown(e, i)}
              type="button"
            >
              {o.label}
            </button>
          ))}
        </div>,
        document.body,
      )}
    </div>
  )
}