<script setup lang="ts">
import { onKeyStroke, useFocus, useMagicKeys } from '@vueuse/core'
import { useFuse } from '@vueuse/integrations/useFuse'
import { computed, ref, watch } from 'vue'

const search = ref('')
const { allLinks } = useNav()
const tags = allLinks.flatMap(item => item.tags)
const matchingTag = computed(() => {
  if (search.value.length < 2) {
    return null
  }
  return tags.find(tag => tag.toLowerCase().startsWith(search.value.toLowerCase()))
})

// search results and popular results
const { results } = useFuse(search, allLinks, {
  fuseOptions: { keys: ['label', 'tags'], includeScore: true, distance: 3 },
  resultLimit: 12,
})
const defaultResults = ['Button', 'Menu', 'Drawer', 'Tabs', 'Dropdown']
  .map(label => allLinks.find(item => item.label === label)!)
  .map(item => ({ item }))
const eitherResults = computed(() => {
  if (results.value.length === 0) {
    return defaultResults
  }
  return results.value
})

// Open menu when searchbox is focused
const isMenuOpen = ref(false)
const searchbox = ref()
const { focused } = useFocus(searchbox)
watch(focused, val => {
  if (val) {
    isMenuOpen.value = val
  } else {
    setTimeout(() => {
      isMenuOpen.value = val
    }, 100)
  }
})

// track the highlighted index in the list
const highlightedIndex = ref(0)
const highlightedItem = computed(() => {
  return eitherResults.value[highlightedIndex.value]?.item
})
// when results change, reset the highlighted index
watch(eitherResults, () => {
  highlightedIndex.value = 0
})

// meta|ctrl+k --> focus the searchbox
const { meta, control } = useMagicKeys()
onKeyStroke('k', () => {
  if (meta?.value || control?.value) {
    focused.value = !focused.value
  }
})

// escape --> close the list
onKeyStroke('Escape', () => {
  if (focused.value) {
    if (search.value === '') {
      focused.value = false
    } else {
      search.value = ''
      highlightedIndex.value = 0
    }
  }
})

// menu click --> close the list
function handleMenuClick() {
  focused.value = false
}

// enter --> navigate to highlighted item
function handleEnter() {
  if (highlightedItem.value) {
    navigateTo(highlightedItem.value.to)
    focused.value = false
    isMenuOpen.value = false
  }
}

// arrow up --> highlighted previous item
function handleArrowUp() {
  highlightedIndex.value = Math.max(highlightedIndex.value - 1, 0)
}
// arrow down --> highlight next item
function handleArrowDown() {
  highlightedIndex.value = Math.min(highlightedIndex.value + 1, eitherResults.value.length - 1)
}
</script>

<template>
  <Dropdown v-model:open="isMenuOpen" :auto-focus="false">
    <DropdownTarget class="relative inline-block">
      <Flex class="absolute z-10 top-0 bottom-0 pointer-events-none items-center pl-2">
        <Icon name="heroicons-solid:search" class="w-5 h-5 opacity-60" aria-hidden="true" />
      </Flex>
      <Input
        id="searchbox"
        ref="searchbox"
        v-model="search"
        placeholder="Search"
        name="search"
        bordered
        class="join-item max-w-[150px] md:max-w-none rounded-xl pl-8"
        onfocus="this.select()"
        @keypress.enter="handleEnter"
        @keydown.arrow-up="handleArrowUp"
        @keydown.arrow-down="handleArrowDown"
      />
      <Flex
        v-if="!focused"
        row
        class="hidden lg:flex items-center flex-nowrap absolute top-0 bottom-0 right-0 pr-2 pointer-events-none opacity-60"
      >
        <Kbd xs>cmd</Kbd>
        <Text xs class="relative bottom-px"> + </Text>
        <Kbd xs>k</Kbd>
      </Flex>
    </DropdownTarget>
    <DropdownContent
      class="bg-base-100 shadow-xl rounded-xl border border-base-300 mt-2 w-full overflow-y-auto max-h-[75vh]"
      @click="handleMenuClick"
    >
      <template #default="{ close }">
        <Menu>
          <MenuTitle v-if="!search.length"> Popular Components </MenuTitle>
          <MenuTitle v-if="search.length && results.length"> Search Results </MenuTitle>

          <MenuItem v-if="search.length && !results.length">
            <span>No Results Found</span>
          </MenuItem>
          <template v-else-if="search.length">
            <MenuItem v-for="(result, index) in results" :key="result.item.label" :active="index === highlightedIndex">
              <NuxtLink :to="result.item.to" @click="close">
                <Icon
                  v-if="(result.item as any).icon"
                  :name="(result.item as any).icon"
                  class="w-5 h-5"
                  aria-hidden="true"
                />
                {{ result.item.label }}
                <Badge v-if="matchingTag && result.item.tags.includes(matchingTag)" warning sm>
                  {{ matchingTag }}
                </Badge>
              </NuxtLink>
            </MenuItem>
          </template>
          <template v-else>
            <MenuItem
              v-for="(result, index) in defaultResults"
              :key="result.item.label"
              :active="index === highlightedIndex"
            >
              <NuxtLink :to="result.item.to" @click="close">
                {{ result.item.label }}
              </NuxtLink>
            </MenuItem>
          </template>
        </Menu>
      </template>
    </DropdownContent>
  </Dropdown>
</template>
