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 [pos, setPos] = useState({ top: 0, left: 0, width: 0 })
  const listboxId = useId()

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

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

  useEffect(() => {
    if (!open) return
    updatePos()
    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)
      }
    }
    function onKey(e: KeyboardEvent) {
      if (e.key === 'Escape') setOpen(false)
    }
    window.addEventListener('scroll', updatePos, true)
    document.addEventListener('mousedown', onDown)
    document.addEventListener('keydown', onKey)
    return () => {
      window.removeEventListener('scroll', updatePos, true)
      document.removeEventListener('mousedown', onDown)
      document.removeEventListener('keydown', onKey)
    }
  }, [open, updatePos])

  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}>
        <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 => (
            <button
              key={String(o.value)}
              role="option"
              aria-selected={String(o.value) === String(value)}
              className={`cv-select-option ${String(o.value) === String(value) ? 'cv-select-option-active' : ''}`}
              onClick={() => { onChange(o.value); setOpen(false) }}
              type="button"
            >
              {o.label}
            </button>
          ))}
        </div>,
        document.body,
      )}
    </div>
  )
}
