<template>
  <section class="flex flex-col gap-2">
    <h3 class="text-xs font-semibold uppercase tracking-wider text-base-content/70 flex items-center gap-1.5">
      <icons-icon-search class="size-3.5 opacity-80" aria-hidden="true" />
      Search
    </h3>
    <div class="relative">
      <input
        ref="inputRef"
        v-model="query"
        type="text"
        class="input input-sm w-full pr-8 focus:ring-2 focus:ring-primary"
        :class="touchFriendly ? 'min-h-11 text-base' : ''"
        placeholder="Coords (x, y) or marker name"
        aria-label="Search by coordinates or marker name"
        aria-expanded="showDropdown && (suggestions.length > 0 || recent.recent.length > 0)"
        aria-haspopup="listbox"
        aria-controls="map-search-listbox"
        autocomplete="off"
        @focus="showDropdown = true"
        @blur="scheduleCloseDropdown"
        @keydown.enter="onEnter"
        @keydown.down.prevent="moveHighlight(1)"
        @keydown.up.prevent="moveHighlight(-1)"
      />
      <button
        v-if="query"
        type="button"
        class="absolute right-2 top-1/2 -translate-y-1/2 btn btn-ghost btn-xs btn-circle"
        aria-label="Clear"
        @mousedown.prevent="query = ''; showDropdown = false"
      >
        ×
      </button>
      <div
        v-if="showDropdown && (suggestions.length > 0 || recent.recent.length > 0)"
        id="map-search-listbox"
        role="listbox"
        class="absolute left-0 right-0 top-full z-[600] mt-1 max-h-56 overflow-y-auto rounded-lg border border-base-300 bg-base-100 shadow-xl py-1"
        :aria-activedescendant="suggestions.length > 0 ? `map-search-opt-${highlightIndex}` : recent.recent.length > 0 ? `map-search-recent-${highlightIndex}` : undefined"
      >
        <template v-if="suggestions.length > 0">
          <button
            v-for="(s, i) in suggestions"
            :id="`map-search-opt-${i}`"
            :key="s.key"
            type="button"
            role="option"
            class="w-full text-left px-3 py-2 text-sm hover:bg-base-200 flex items-center gap-2"
            :class="{ 'bg-primary/10': i === highlightIndex }"
            :aria-selected="i === highlightIndex"
            @mousedown.prevent="goToSuggestion(s)"
          >
            <icons-icon-map-pin class="size-4 shrink-0 opacity-70" />
            <span class="truncate">{{ s.label }}</span>
          </button>
        </template>
        <template v-else-if="recent.recent.length > 0">
          <p class="px-3 py-1 text-xs text-base-content/60 uppercase tracking-wider">Recent</p>
          <button
            v-for="(r, i) in recent.recent"
            :id="`map-search-recent-${i}`"
            :key="r.at"
            type="button"
            role="option"
            class="w-full text-left px-3 py-2 text-sm hover:bg-base-200 flex items-center gap-2"
            :class="{ 'bg-primary/10': i === highlightIndex }"
            :aria-selected="i === highlightIndex"
            @mousedown.prevent="goToRecent(r)"
          >
            <icons-icon-map-pin class="size-4 shrink-0 opacity-70" />
            <span class="truncate">{{ r.label || `${r.mapId} · ${r.x}, ${r.y}` }}</span>
          </button>
        </template>
      </div>
    </div>
  </section>
</template>

<script setup lang="ts">
import type { MapInfo } from '~/types/api'
import { useMapNavigate } from '~/composables/useMapNavigate'
import { useRecentLocations } from '~/composables/useRecentLocations'

const props = withDefaults(
  defineProps<{
    maps: MapInfo[]
    questGivers: Array<{ id: number; name: string }>
    currentMapId: number
    currentCoords: { x: number; y: number; z: number } | null
    touchFriendly?: boolean
  }>(),
  { touchFriendly: false }
)

const { goToCoords } = useMapNavigate()
const recent = useRecentLocations()

const inputRef = ref<HTMLInputElement | null>(null)
const query = ref('')
const queryDebounced = ref('')
let queryDebounceId: ReturnType<typeof setTimeout> | null = null
const SEARCH_DEBOUNCE_MS = 180
watch(
  query,
  (v) => {
    if (queryDebounceId) clearTimeout(queryDebounceId)
    queryDebounceId = setTimeout(() => {
      queryDebounced.value = v
      queryDebounceId = null
    }, SEARCH_DEBOUNCE_MS)
  },
  { immediate: true }
)
const showDropdown = ref(false)
const highlightIndex = ref(0)
let closeDropdownTimer: ReturnType<typeof setTimeout> | null = null

interface Suggestion {
  key: string
  label: string
  mapId: number
  x: number
  y: number
  zoom?: number
  markerId?: number
}

const suggestions = computed<Suggestion[]>(() => {
  const q = queryDebounced.value.trim().toLowerCase()
  if (!q) return []

  const coordMatch = q.match(/^\s*(-?\d+)\s*[,;\s]\s*(-?\d+)\s*$/)
  if (coordMatch) {
    const x = parseInt(coordMatch[1], 10)
    const y = parseInt(coordMatch[2], 10)
    return [
      {
        key: `coord-${x}-${y}`,
        label: `Go to ${x}, ${y}`,
        mapId: props.currentMapId,
        x,
        y,
        zoom: props.currentCoords?.z,
      },
    ]
  }

  const list: Suggestion[] = []
  for (const qg of props.questGivers) {
    if (qg.name.toLowerCase().includes(q)) {
      list.push({
        key: `qg-${qg.id}`,
        label: qg.name,
        mapId: props.currentMapId,
        x: 0,
        y: 0,
        zoom: undefined,
        markerId: qg.id,
      })
    }
  }
  if (list.length > 0) return list.slice(0, 8)

  return []
})

function scheduleCloseDropdown() {
  closeDropdownTimer = setTimeout(() => {
    showDropdown.value = false
    closeDropdownTimer = null
  }, 150)
}

function moveHighlight(delta: number) {
  const total = suggestions.value.length || recent.recent.length
  if (total === 0) return
  highlightIndex.value = (highlightIndex.value + delta + total) % total
}

function goToSuggestion(s: Suggestion) {
  showDropdown.value = false
  if (s.key.startsWith('coord-')) {
    goToCoords(s.mapId, s.x, s.y, s.zoom)
    recent.push({ mapId: s.mapId, x: s.x, y: s.y, zoom: s.zoom, label: `${s.x}, ${s.y}` })
  } else if (s.markerId != null) {
    emit('jump-to-marker', s.markerId)
  }
}

function goToRecent(r: (typeof recent.recent.value)[0]) {
  showDropdown.value = false
  goToCoords(r.mapId, r.x, r.y, r.zoom)
}

function onEnter() {
  if (suggestions.value.length > 0) {
    const s = suggestions.value[highlightIndex.value] ?? suggestions.value[0]
    goToSuggestion(s)
  } else if (recent.recent.length > 0) {
    const r = recent.recent[highlightIndex.value] ?? recent.recent[0]
    goToRecent(r)
  }
}

const emit = defineEmits<{
  'jump-to-marker': [id: number]
}>()

watch(suggestions, () => {
  highlightIndex.value = 0
}, { flush: 'sync' })

onMounted(() => {
  useMapNavigate().setFocusSearch(() => inputRef.value?.focus())
})

onBeforeUnmount(() => {
  if (queryDebounceId) clearTimeout(queryDebounceId)
})

onBeforeUnmount(() => {
  if (closeDropdownTimer) clearTimeout(closeDropdownTimer)
  useMapNavigate().setFocusSearch(null)
})
</script>
