import React, { useEffect, useId, useMemo, useRef, useState } from 'react'
import { createPortal } from 'react-dom'

function normalizeSearchText(value) {
  return String(value || '')
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '')
    .toLowerCase()
    .trim()
}

function normalizeOption(option) {
  if (typeof option === 'string') {
    const text = String(option || '').trim()
    return text ? { value: text, label: text, secondary: '' } : null
  }
  if (!option || typeof option !== 'object') return null
  const rawValue = option.value ?? option.id ?? option.key ?? ''
  const value = String(rawValue || '').trim()
  if (!value) return null
  const label = String(option.label ?? option.nome_modelo ?? option.arquivo ?? value).trim() || value
  const secondary = String(option.secondary ?? option.arquivo ?? '').trim()
  return { value, label, secondary }
}

function scoreOptionMatch(option, queryNormalized) {
  if (!queryNormalized) return 1

  const labelNorm = normalizeSearchText(option?.label || '')
  const secondaryNorm = normalizeSearchText(option?.secondary || '')
  const labelTokens = labelNorm.split(/\s+/).filter(Boolean)
  const secondaryTokens = secondaryNorm.split(/\s+/).filter(Boolean)

  if (labelNorm === queryNormalized) return 400
  if (labelTokens.includes(queryNormalized)) return 320
  if (labelNorm.startsWith(queryNormalized)) return 280
  if (labelTokens.some((token) => token.startsWith(queryNormalized))) return 220
  if (labelNorm.includes(queryNormalized)) return 180
  if (secondaryNorm === queryNormalized) return 160
  if (secondaryTokens.includes(queryNormalized)) return 140
  if (secondaryNorm.includes(queryNormalized)) return 120
  return 0
}

export default function SinglePillAutocomplete({
  value,
  onChange,
  options = [],
  placeholder = 'Selecione um item',
  panelTitle = '',
  emptyMessage = 'Nenhuma sugestao encontrada.',
  loading = false,
  disabled = false,
  onOpenChange = null,
  inputName = '',
  inputAutoComplete = 'new-password',
}) {
  const rootRef = useRef(null)
  const inputRef = useRef(null)
  const panelRef = useRef(null)
  const [query, setQuery] = useState('')
  const [open, setOpen] = useState(false)
  const [activeIndex, setActiveIndex] = useState(-1)
  const [panelStyle, setPanelStyle] = useState({})
  const generatedInputId = useId()
  const resolvedInputName = inputName || `mesa_single_${String(generatedInputId).replace(/[^a-zA-Z0-9_-]/g, '')}`

  const selectedValue = String(value || '')
  const normalizedOptions = useMemo(() => {
    const unique = []
    const seen = new Set()
    ;(options || []).forEach((item) => {
      const normalized = normalizeOption(item)
      if (!normalized) return
      if (seen.has(normalized.value)) return
      seen.add(normalized.value)
      unique.push(normalized)
    })
    return unique
  }, [options])

  const selectedOption = useMemo(
    () => normalizedOptions.find((item) => item.value === selectedValue) || null,
    [normalizedOptions, selectedValue],
  )

  const queryNormalized = normalizeSearchText(query)
  const filteredOptions = useMemo(() => {
    if (loading) return []
    if (!queryNormalized) return normalizedOptions.slice(0, 160)
    return normalizedOptions
      .map((item, index) => ({ item, index, score: scoreOptionMatch(item, queryNormalized) }))
      .filter((entry) => entry.score > 0)
      .sort((a, b) => {
        if (b.score !== a.score) return b.score - a.score
        return a.index - b.index
      })
      .map((entry) => entry.item)
      .slice(0, 160)
  }, [loading, normalizedOptions, queryNormalized])

  useEffect(() => {
    if (!open) return undefined
    function onDocumentMouseDown(event) {
      const target = event.target
      if (rootRef.current?.contains(target) || panelRef.current?.contains(target)) return
      setOpen(false)
    }
    document.addEventListener('mousedown', onDocumentMouseDown)
    return () => document.removeEventListener('mousedown', onDocumentMouseDown)
  }, [open])

  useEffect(() => {
    if (typeof onOpenChange !== 'function') return
    onOpenChange(Boolean(open && !disabled))
  }, [open, disabled, onOpenChange])

  useEffect(() => () => {
    if (typeof onOpenChange === 'function') onOpenChange(false)
  }, [onOpenChange])

  useEffect(() => {
    if (!open || filteredOptions.length === 0) {
      setActiveIndex(-1)
      return
    }
    if (activeIndex >= filteredOptions.length) setActiveIndex(filteredOptions.length - 1)
  }, [activeIndex, filteredOptions, open])

  function updatePanelPosition() {
    if (!open || !rootRef.current || !panelRef.current || typeof window === 'undefined') return

    const bounds = rootRef.current.getBoundingClientRect()
    const panel = panelRef.current
    const viewportWidth = window.innerWidth || document.documentElement.clientWidth || 0
    const viewportHeight = window.innerHeight || document.documentElement.clientHeight || 0
    const padding = 12
    const gap = 6

    function clamp(value, min, max) {
      if (max < min) return min
      return Math.min(Math.max(value, min), max)
    }

    const width = clamp(bounds.width, 220, Math.max(220, viewportWidth - (padding * 2)))
    const availableBelow = Math.max(120, viewportHeight - bounds.bottom - gap - padding)
    const availableAbove = Math.max(120, bounds.top - gap - padding)
    const shouldOpenBelow = availableBelow >= 180 || availableBelow >= availableAbove
    const maxHeight = shouldOpenBelow ? availableBelow : availableAbove
    const desiredHeight = Math.min(panel.scrollHeight || 220, maxHeight)
    const left = clamp(bounds.left, padding, viewportWidth - padding - width)
    const top = shouldOpenBelow
      ? clamp(bounds.bottom + gap, padding, viewportHeight - padding - desiredHeight)
      : clamp(bounds.top - gap - desiredHeight, padding, viewportHeight - padding - desiredHeight)

    setPanelStyle({
      left: `${left}px`,
      top: `${top}px`,
      width: `${width}px`,
      maxHeight: `${Math.max(120, maxHeight)}px`,
    })
  }

  useEffect(() => {
    if (!open) return undefined

    const rafId = window.requestAnimationFrame(() => {
      updatePanelPosition()
    })

    function handleViewportChange() {
      updatePanelPosition()
    }

    window.addEventListener('resize', handleViewportChange)
    window.addEventListener('scroll', handleViewportChange, true)

    return () => {
      window.cancelAnimationFrame(rafId)
      window.removeEventListener('resize', handleViewportChange)
      window.removeEventListener('scroll', handleViewportChange, true)
    }
  }, [open, filteredOptions.length, loading, query, selectedValue])

  function emitChange(nextValue) {
    if (typeof onChange === 'function') onChange(String(nextValue || ''))
  }

  function selectOption(option) {
    if (!option) return
    emitChange(option.value)
    setQuery('')
    setOpen(false)
    setActiveIndex(-1)
  }

  function clearSelection(event) {
    event.preventDefault()
    event.stopPropagation()
    emitChange('')
    setQuery('')
    setOpen(true)
    setActiveIndex(-1)
    window.requestAnimationFrame(() => inputRef.current?.focus())
  }

  function onInputChange(event) {
    if (disabled) return
    setQuery(event.target.value)
    setOpen(true)
    setActiveIndex(-1)
  }

  function onInputFocus() {
    if (disabled) return
    setOpen(true)
    setActiveIndex(-1)
  }

  function onInputKeyDown(event) {
    if (disabled) return

    if (event.key === 'Escape') {
      setOpen(false)
      return
    }

    if (event.key === 'Backspace' && !query && selectedOption) {
      emitChange('')
      setOpen(true)
      return
    }

    if (!filteredOptions.length) return

    if (event.key === 'ArrowDown') {
      event.preventDefault()
      setOpen(true)
      setActiveIndex((prev) => (prev < 0 ? 0 : (prev + 1) % filteredOptions.length))
      return
    }

    if (event.key === 'ArrowUp') {
      event.preventDefault()
      setOpen(true)
      setActiveIndex((prev) => {
        if (prev < 0) return filteredOptions.length - 1
        return (prev - 1 + filteredOptions.length) % filteredOptions.length
      })
      return
    }

    if (event.key === 'Enter') {
      event.preventDefault()
      if (activeIndex >= 0 && activeIndex < filteredOptions.length) {
        selectOption(filteredOptions[activeIndex])
        return
      }
      if (filteredOptions.length === 1) {
        selectOption(filteredOptions[0])
      }
    }
  }

  return (
    <div className={`chip-autocomplete chip-autocomplete-single${open ? ' is-open' : ''}${disabled ? ' is-disabled' : ''}`} ref={rootRef}>
      <div className={`chip-autocomplete-single-control${disabled ? ' is-disabled' : ''}`}>
        {selectedOption ? (
          <span className="chip-autocomplete-selected chip-autocomplete-selected-inline">
            <span>{selectedOption.label}</span>
            {!disabled ? (
              <button
                type="button"
                className="chip-autocomplete-selected-remove"
                onMouseDown={clearSelection}
                onClick={(event) => {
                  event.preventDefault()
                  event.stopPropagation()
                }}
                aria-label={`Remover ${selectedOption.label}`}
              >
                ×
              </button>
            ) : null}
          </span>
        ) : null}
        <input
          ref={inputRef}
          type="text"
          className="chip-autocomplete-single-input"
          name={resolvedInputName}
          value={query}
          onChange={onInputChange}
          onFocus={onInputFocus}
          onKeyDown={onInputKeyDown}
          placeholder={selectedOption ? '' : placeholder}
          autoComplete={inputAutoComplete}
          autoCorrect="off"
          autoCapitalize="none"
          spellCheck={false}
          disabled={disabled}
          data-lpignore="true"
          data-1p-ignore="true"
        />
      </div>

      {open && !disabled && typeof document !== 'undefined'
        ? createPortal(
          <div
            ref={panelRef}
            className="chip-autocomplete-panel is-floating"
            style={{ ...panelStyle, visibility: panelStyle.left ? 'visible' : 'hidden' }}
            role="listbox"
          >
            {panelTitle ? <div className="chip-autocomplete-panel-head">{panelTitle}</div> : null}
            {loading ? (
              <div className="chip-autocomplete-empty">Carregando lista...</div>
            ) : filteredOptions.length ? (
              <div className="chip-autocomplete-chip-wrap">
                {filteredOptions.map((item, idx) => (
                  <button
                    type="button"
                    key={`single-chip-${item.value}-${idx}`}
                    className={`chip-autocomplete-chip${idx === activeIndex ? ' is-active' : ''}`}
                    onMouseDown={(event) => {
                      event.preventDefault()
                      event.stopPropagation()
                      selectOption(item)
                    }}
                    title={item.secondary ? `${item.label} | ${item.secondary}` : item.label}
                  >
                    {item.label}
                  </button>
                ))}
              </div>
            ) : (
              <div className="chip-autocomplete-empty">
                {emptyMessage}
              </div>
            )}
          </div>,
          document.body,
        )
        : null}
    </div>
  )
}
