'use client'

import { useEffect, useMemo, useRef, useState } from 'react'
import { MapPin } from 'lucide-react'

export interface LocationSuggestion {
  id: string
  path: string
}

interface Props {
  value: string
  onChange: (v: string) => void
  onCommit?: () => void
  suggestions: LocationSuggestion[]
  placeholder?: string
  autoFocus?: boolean
}

const SEP_REGEX = /\s*[>/]\s*|\s+-\s+/
const TRAILING_SEP = /(\s*[>/]\s*|\s+-\s+)\s*$/

function splitSegments(input: string): string[] {
  return input.split(SEP_REGEX).map(s => s.trim()).filter(Boolean)
}

// "Garage > " is a complete-child marker — the user has separated and wants
// to type/pick the next segment.
function endsWithSeparator(input: string): boolean {
  return TRAILING_SEP.test(input)
}

function highlight(label: string, query: string): React.ReactNode {
  if (!query) return label
  const i = label.toLowerCase().indexOf(query.toLowerCase())
  if (i < 0) return label
  return (
    <>
      {label.slice(0, i)}
      <span className="font-semibold text-primary">{label.slice(i, i + query.length)}</span>
      {label.slice(i + query.length)}
    </>
  )
}

// Score paths: lower is better.
//   - Exact prefix matches beat substring matches.
//   - Within the same tier, preserve the input order (already sorted by recency
//     when the caller provides suggestions).
function rank(input: string, suggestions: LocationSuggestion[]): LocationSuggestion[] {
  const q = input.trim().toLowerCase()
  if (!q) return suggestions
  return [...suggestions]
    .map((s, idx) => {
      const p = s.path.toLowerCase()
      let score = 1000
      if (p === q) score = 0
      else if (p.startsWith(q)) score = 100
      else if (p.includes(q)) score = 200
      return { s, score, idx }
    })
    .filter(x => x.score < 1000)
    .sort((a, b) => a.score - b.score || a.idx - b.idx)
    .map(x => x.s)
}

function computeMatches(input: string, suggestions: LocationSuggestion[]): LocationSuggestion[] {
  const trimmed = input.trim()
  // The dropdown is already scrollable (max-h-72) so no artificial cap —
  // any cap would silently hide locations that scroll past the fold.
  if (!trimmed) return suggestions

  // Segment-aware: if the user is building a structured path, prefer
  // descendants of the parent over arbitrary substring matches elsewhere.
  const segments = splitSegments(trimmed)
  const completeChild = endsWithSeparator(trimmed)
  const parentSegments = completeChild ? segments : segments.slice(0, -1)
  const lastPartial = completeChild ? '' : (segments[segments.length - 1] || '').toLowerCase()

  if (parentSegments.length > 0) {
    const parentPathLower = parentSegments.join(' > ').toLowerCase()
    const descendants = suggestions.filter(s => {
      const sp = s.path.toLowerCase()
      if (!sp.startsWith(parentPathLower + ' > ')) return false
      const rest = sp.slice(parentPathLower.length + 3)
      return rest.startsWith(lastPartial)
    })
    if (descendants.length > 0) return descendants
  }

  return rank(trimmed, suggestions)
}

export function LocationAutocomplete({
  value,
  onChange,
  onCommit,
  suggestions,
  placeholder,
  autoFocus,
}: Props) {
  const [open, setOpen] = useState(false)
  const [highlightIdx, setHighlightIdx] = useState(0)
  const inputRef = useRef<HTMLInputElement>(null)
  const wrapRef = useRef<HTMLDivElement>(null)

  const matches = useMemo(() => computeMatches(value, suggestions), [value, suggestions])
  // Clamp the highlight to the current match count rather than resetting it
  // via useEffect (which would trigger a cascading render).
  const safeHighlight = Math.min(highlightIdx, Math.max(0, matches.length - 1))

  useEffect(() => {
    const onDocClick = (e: MouseEvent) => {
      if (!wrapRef.current) return
      if (!wrapRef.current.contains(e.target as Node)) setOpen(false)
    }
    document.addEventListener('mousedown', onDocClick)
    return () => document.removeEventListener('mousedown', onDocClick)
  }, [])

  const pick = (path: string) => {
    onChange(path)
    setOpen(false)
    inputRef.current?.focus()
  }

  return (
    <div ref={wrapRef} className="relative">
      <input
        ref={inputRef}
        value={value}
        onChange={e => {
          onChange(e.target.value)
          setOpen(true)
        }}
        onFocus={() => setOpen(true)}
        onKeyDown={e => {
          if (e.key === 'ArrowDown') {
            e.preventDefault()
            setOpen(true)
            setHighlightIdx(i => Math.min(i + 1, matches.length - 1))
          } else if (e.key === 'ArrowUp') {
            e.preventDefault()
            setHighlightIdx(i => Math.max(i - 1, 0))
          } else if (e.key === 'Enter') {
            if (open && matches[safeHighlight]) {
              e.preventDefault()
              pick(matches[safeHighlight].path)
            } else if (onCommit) {
              e.preventDefault()
              onCommit()
            }
          } else if (e.key === 'Escape') {
            setOpen(false)
          } else if (e.key === 'Tab' && open && matches[safeHighlight]) {
            e.preventDefault()
            pick(matches[safeHighlight].path)
          }
        }}
        autoFocus={autoFocus}
        autoCapitalize="words"
        autoComplete="off"
        placeholder={placeholder}
        className="flex h-11 w-full rounded-lg border border-input bg-background px-3 py-2 text-base focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
      />
      {open && matches.length > 0 && (
        <div className="absolute z-30 left-0 right-0 mt-1 rounded-lg border bg-popover text-popover-foreground shadow-lg overflow-hidden">
          <ul role="listbox" className="max-h-72 overflow-y-auto py-1">
            {matches.map((m, i) => (
              <li
                key={m.id}
                role="option"
                aria-selected={i === safeHighlight}
                onMouseDown={e => {
                  // mousedown (not click) so the input doesn't blur first and
                  // close the popover before pick fires.
                  e.preventDefault()
                  pick(m.path)
                }}
                onMouseEnter={() => setHighlightIdx(i)}
                className={`flex items-center gap-2 px-3 py-2 text-sm cursor-pointer ${
                  i === safeHighlight ? 'bg-accent text-accent-foreground' : ''
                }`}
              >
                <MapPin className="size-3.5 text-muted-foreground shrink-0" />
                <span className="truncate">{highlight(m.path, value.trim())}</span>
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  )
}
