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

const { isSearchOpen, openSearch, closeSearch } = useSearch()
const { themes, setTheme } = useDaisyTheme()

// Handle escape key
onKeyStroke('Escape', () => {
  if (isSearchOpen.value) {
    closeSearch()
  }
})

// meta|ctrl+k --> toggle the search modal
const { meta, control } = useMagicKeys()
onKeyStroke('k', e => {
  if (meta?.value || control?.value) {
    e.preventDefault()
    openSearch()
  }
})

const search = ref('')
const searchInput = ref<HTMLInputElement | null>(null)
const { allLinks } = useNav()

// Build theme search items
const themeItems = themes.value
  .filter(t => t.theme !== 'system')
  .map(t => ({
    label: t.theme,
    to: '',
    tags: ['theme'],
    isTheme: true,
  }))

const allSearchItems = [...allLinks, ...themeItems]

const tags = allSearchItems.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, allSearchItems, {
  fuseOptions: { keys: ['label', 'tags'], includeScore: true, distance: 3 },
  resultLimit: 12,
})

// Split results into nav links and themes
const navResults = computed(() => results.value.filter(r => !(r.item as any).isTheme))
const themeResults = computed(() => {
  const fromFuse = results.value.filter(r => (r.item as any).isTheme)
  // If user typed "theme", show all themes (fuse's resultLimit caps at 12)
  if (fromFuse.length > 0 && matchingTag.value === 'theme') {
    return themeItems.map(item => ({ item }))
  }
  return fromFuse
})

const popularResults = ['Button', 'Menu', 'Drawer', 'Tabs', 'Dropdown']
  .map(label => allLinks.find(item => item.label === label)!)
  .filter(Boolean)
  .map(item => ({ item }))

const newResults = allLinks
  .filter(item => item.tags?.includes('new'))
  .slice(0, 5)
  .map(item => ({ item }))

// Combined default results for keyboard navigation
const allDefaultResults = computed(() => [...popularResults, ...newResults])

const eitherResults = computed(() => {
  if (results.value.length === 0) {
    return allDefaultResults.value
  }
  return [...navResults.value, ...themeResults.value]
})

// track the highlighted index in the list
const highlightedIndex = ref(0)
const resultsContainer = ref<HTMLElement | null>(null)
const highlightedItem = computed(() => {
  return eitherResults.value[highlightedIndex.value]?.item
})

// scroll highlighted item into view
watch(highlightedIndex, () => {
  nextTick(() => {
    const el = resultsContainer.value?.querySelector('[data-highlighted]')
    el?.scrollIntoView({ block: 'nearest' })
  })
})

// when results change, reset the highlighted index
watch(eitherResults, () => {
  highlightedIndex.value = 0
})

// Focus input when modal opens
watch(isSearchOpen, val => {
  if (val) {
    nextTick(() => {
      searchInput.value?.focus()
    })
  } else {
    search.value = ''
    highlightedIndex.value = 0
  }
})

// enter --> navigate to highlighted item or apply theme
function handleEnter() {
  if (highlightedItem.value) {
    if ((highlightedItem.value as any).isTheme) {
      setTheme(highlightedItem.value.label)
    } else {
      navigateTo(highlightedItem.value.to)
    }
    closeSearch()
  }
}

function handleThemeClick(themeName: string) {
  setTheme(themeName)
  closeSearch()
}

// 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)
}

function handleResultClick(to: string) {
  navigateTo(to)
  closeSearch()
}
</script>

<template>
  <ClientOnly>
    <Teleport to="body">
      <Modal v-model="isSearchOpen">
        <ModalBox class="p-0 max-w-lg overflow-hidden">
          <!-- Search Input -->
          <div class="flex items-center gap-3 p-4 border-b border-base-300">
            <Icon name="heroicons-solid:search" class="size-5 opacity-60 shrink-0" />
            <input
              ref="searchInput"
              v-model="search"
              type="text"
              placeholder="Search components, docs..."
              class="flex-1 bg-transparent outline-none text-lg"
              @keydown.enter="handleEnter"
              @keydown.arrow-up.prevent="handleArrowUp"
              @keydown.arrow-down.prevent="handleArrowDown"
            />
            <Kbd sm class="opacity-50">ESC</Kbd>
          </div>

          <!-- Results -->
          <div ref="resultsContainer" class="max-h-[60vh] overflow-y-auto">
            <div v-if="!search.length" class="p-2">
              <Text sm class="px-3 py-2 opacity-50 uppercase tracking-wider">Popular</Text>
              <Menu class="w-full">
                <MenuItem
                  v-for="(result, index) in popularResults"
                  :key="result.item.label"
                  :class="{ 'bg-primary/10': index === highlightedIndex }"
                  :data-highlighted="index === highlightedIndex ? '' : undefined"
                >
                  <a class="flex items-center gap-3" @click="handleResultClick(result.item.to)">
                    <Icon
                      v-if="(result.item as any).icon"
                      :name="(result.item as any).icon"
                      class="size-5 opacity-60"
                    />
                    <span>{{ result.item.label }}</span>
                  </a>
                </MenuItem>
              </Menu>

              <Text sm class="px-3 py-2 mt-4 opacity-50 uppercase tracking-wider">New</Text>
              <Menu class="w-full">
                <MenuItem
                  v-for="(result, index) in newResults"
                  :key="result.item.label"
                  :class="{ 'bg-primary/10': index + popularResults.length === highlightedIndex }"
                  :data-highlighted="index + popularResults.length === highlightedIndex ? '' : undefined"
                >
                  <a class="flex items-center gap-3" @click="handleResultClick(result.item.to)">
                    <Icon
                      v-if="(result.item as any).icon"
                      :name="(result.item as any).icon"
                      class="size-5 opacity-60"
                    />
                    <span class="flex-1">{{ result.item.label }}</span>
                    <Badge primary sm>New</Badge>
                  </a>
                </MenuItem>
              </Menu>
            </div>

            <div v-else-if="results.length" class="p-2">
              <template v-if="navResults.length">
                <Text sm class="px-3 py-2 opacity-50 uppercase tracking-wider">Results</Text>
                <Menu class="w-full">
                  <MenuItem
                    v-for="(result, index) in navResults"
                    :key="result.item.label"
                    :class="{ 'bg-primary/10': index === highlightedIndex }"
                    :data-highlighted="index === highlightedIndex ? '' : undefined"
                  >
                    <a class="flex items-center gap-3" @click="handleResultClick(result.item.to)">
                      <Icon
                        v-if="(result.item as any).icon"
                        :name="(result.item as any).icon"
                        class="size-5 opacity-60"
                      />
                      <span class="flex-1">{{ result.item.label }}</span>
                      <Badge v-if="matchingTag && result.item.tags.includes(matchingTag)" warning sm>
                        {{ matchingTag }}
                      </Badge>
                    </a>
                  </MenuItem>
                </Menu>
              </template>

              <template v-if="themeResults.length">
                <Text sm class="px-3 py-2 opacity-50 uppercase tracking-wider" :class="{ 'mt-4': navResults.length }">
                  Themes
                </Text>
                <Menu class="w-full">
                  <li
                    v-for="(result, index) in themeResults"
                    :key="result.item.label"
                    :data-theme="result.item.label"
                    :data-highlighted="index + navResults.length === highlightedIndex ? '' : undefined"
                  >
                    <a
                      class="flex items-center gap-3"
                      :style="{ backgroundColor: 'var(--color-base-100)', color: 'var(--color-base-content)' }"
                      :class="{ 'bg-primary! text-primary-content!': index + navResults.length === highlightedIndex }"
                      @click="handleThemeClick(result.item.label)"
                    >
                      <ThemeTile :theme="result.item.label" xs class="size-5!" />
                      <span class="flex-1">{{ result.item.label }}</span>
                    </a>
                  </li>
                </Menu>
              </template>
            </div>

            <div v-else class="p-8 text-center">
              <Icon name="heroicons-outline:search" class="size-12 opacity-20 mx-auto mb-4" />
              <Text class="opacity-60">No results found for "{{ search }}"</Text>
              <Text sm class="opacity-40 mt-1">Try searching for something else</Text>
            </div>
          </div>

          <!-- Footer -->
          <div class="flex items-center gap-4 p-3 border-t border-base-300 text-xs opacity-50">
            <span class="flex items-center gap-1">
              <Kbd xs>↑</Kbd>
              <Kbd xs>↓</Kbd>
              to navigate
            </span>
            <span class="flex items-center gap-1">
              <Kbd xs>↵</Kbd>
              to select
            </span>
            <span class="flex items-center gap-1">
              <Kbd xs>esc</Kbd>
              to close
            </span>
          </div>
        </ModalBox>
      </Modal>
    </Teleport>
  </ClientOnly>
</template>
