<script setup lang="ts">
import { ref, computed, onBeforeUnmount, nextTick } from 'vue'
import { X, Plus } from 'lucide-vue-next'
import { useTagsStore } from '../stores/tags'
import { computeDropdownPosition } from '../utils/dropdown'

interface Props {
  modelValue: number[]
}

const props = defineProps<Props>()
const emit = defineEmits<{
  'update:modelValue': [value: number[]]
}>()

const tagsStore = useTagsStore()

const isOpen = ref(false)
const searchQuery = ref('')
const triggerRef = ref<HTMLDivElement | null>(null)
const panelRef = ref<HTMLDivElement | null>(null)
const panelStyle = ref<Record<string, string>>({})
const inputRef = ref<HTMLInputElement | null>(null)
const highlightedIndex = ref(-1)

const selectedTags = computed(() => {
  return tagsStore.tags.filter(t => t.id && props.modelValue.includes(t.id))
})

const filteredTags = computed(() => {
  const q = searchQuery.value.toLowerCase()
  return tagsStore.tags.filter(t => {
    if (t.id && props.modelValue.includes(t.id)) return false
    if (q && !t.name.toLowerCase().includes(q)) return false
    return true
  })
})

const showCreateOption = computed(() => {
  if (!searchQuery.value.trim()) return false
  return !tagsStore.tags.some(t => t.name.toLowerCase() === searchQuery.value.trim().toLowerCase())
})

function toggleTag(tagId: number) {
  const current = [...props.modelValue]
  const index = current.indexOf(tagId)
  if (index >= 0) {
    current.splice(index, 1)
  } else {
    current.push(tagId)
  }
  emit('update:modelValue', current)
}

function removeTag(tagId: number) {
  emit('update:modelValue', props.modelValue.filter(id => id !== tagId))
}

async function createAndAdd() {
  const name = searchQuery.value.trim()
  if (!name) return
  const colors = ['#3B82F6', '#8B5CF6', '#EC4899', '#10B981', '#EF4444', '#06B6D4', '#F59E0B', '#6B7280']
  const color = colors[tagsStore.tags.length % colors.length]
  const id = await tagsStore.createTag({ name, color })
  if (id) {
    emit('update:modelValue', [...props.modelValue, id])
    searchQuery.value = ''
  }
}

function updatePosition() {
  if (!triggerRef.value) return
  panelStyle.value = computeDropdownPosition(triggerRef.value, { minWidth: 200, estimatedHeight: 200, panelEl: panelRef.value })
}

function open() {
  isOpen.value = true
  searchQuery.value = ''
  updatePosition()
  nextTick(() => {
    updatePosition()
    inputRef.value?.focus()
  })
  document.addEventListener('click', onClickOutside, true)
  document.addEventListener('scroll', onScrollOrResize, true)
  window.addEventListener('resize', onScrollOrResize)
}

function close() {
  isOpen.value = false
  document.removeEventListener('click', onClickOutside, true)
  document.removeEventListener('scroll', onScrollOrResize, true)
  window.removeEventListener('resize', onScrollOrResize)
}

function onClickOutside(e: MouseEvent) {
  const target = e.target as Node
  if (triggerRef.value?.contains(target) || panelRef.value?.contains(target)) return
  close()
}

function onScrollOrResize() {
  if (isOpen.value) updatePosition()
}

function onSearchKeydown(e: KeyboardEvent) {
  const total = filteredTags.value.length + (showCreateOption.value ? 1 : 0)
  if (e.key === 'ArrowDown') {
    e.preventDefault()
    highlightedIndex.value = Math.min(highlightedIndex.value + 1, total - 1)
  } else if (e.key === 'ArrowUp') {
    e.preventDefault()
    highlightedIndex.value = Math.max(highlightedIndex.value - 1, -1)
  } else if (e.key === 'Enter' && highlightedIndex.value >= 0) {
    e.preventDefault()
    if (highlightedIndex.value < filteredTags.value.length) {
      const tag = filteredTags.value[highlightedIndex.value]
      toggleTag(tag.id!)
      searchQuery.value = ''
    } else if (showCreateOption.value) {
      createAndAdd()
    }
    highlightedIndex.value = -1
  } else if (e.key === 'Escape') {
    e.preventDefault()
    close()
  }
}

onBeforeUnmount(() => {
  document.removeEventListener('click', onClickOutside, true)
  document.removeEventListener('scroll', onScrollOrResize, true)
  window.removeEventListener('resize', onScrollOrResize)
})
</script>

<template>
  <div ref="triggerRef" class="relative">
    <!-- Selected tags + add button -->
    <TransitionGroup tag="div" name="chip" class="flex flex-wrap items-center gap-1.5">
      <span
        v-for="tag in selectedTags"
        :key="tag.id"
        class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[0.6875rem] text-text-primary"
        :style="{ backgroundColor: tag.color + '22', borderColor: tag.color + '44', border: '1px solid' }"
      >
        <span class="w-1.5 h-1.5 rounded-full" :style="{ backgroundColor: tag.color }" aria-hidden="true" />
        {{ tag.name }}
        <button @click.stop="removeTag(tag.id!)" :aria-label="'Remove tag ' + tag.name" v-tooltip="'Remove tag'" class="ml-0.5 min-w-[24px] min-h-[24px] flex items-center justify-center hover:text-status-error">
          <X class="w-2.5 h-2.5" aria-hidden="true" />
        </button>
      </span>
      <button
        key="__add_btn__"
        type="button"
        @click="isOpen ? close() : open()"
        aria-label="Add tag"
        :aria-expanded="isOpen"
        class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[0.6875rem] text-text-tertiary border border-border-subtle hover:text-text-secondary hover:border-border-visible transition-colors"
      >
        <Plus class="w-3 h-3" aria-hidden="true" />
        Tag
      </button>
    </TransitionGroup>

    <!-- Dropdown -->
    <Teleport to="#app">
      <Transition name="dropdown">
      <div
        v-if="isOpen"
        ref="panelRef"
        :style="panelStyle"
        role="listbox"
        aria-label="Tag options"
        class="bg-bg-surface border border-border-visible rounded-xl shadow-[0_4px_24px_rgba(0,0,0,0.4)] overflow-hidden"
      >
        <div class="px-2 pt-2 pb-1">
          <input
            ref="inputRef"
            v-model="searchQuery"
            type="text"
            aria-label="Search or create tag"
            @keydown="onSearchKeydown"
            class="w-full px-2.5 py-1.5 bg-bg-inset border border-border-subtle rounded-lg text-[0.75rem] text-text-primary placeholder-text-tertiary focus:outline-none focus:border-border-visible"
            placeholder="Search or create tag..."
          />
        </div>
        <div class="max-h-[160px] overflow-y-auto py-1">
          <div
            v-for="(tag, index) in filteredTags"
            :key="tag.id"
            :id="'tag-option-' + tag.id"
            role="option"
            @click="toggleTag(tag.id!); searchQuery = ''"
            class="flex items-center gap-2 px-3 py-1.5 cursor-pointer hover:bg-bg-elevated transition-colors"
            :class="{ 'bg-bg-elevated': index === highlightedIndex }"
          >
            <span class="w-2 h-2 rounded-full" :style="{ backgroundColor: tag.color }" aria-hidden="true" />
            <span class="text-[0.75rem] text-text-primary">{{ tag.name }}</span>
          </div>
          <div
            v-if="showCreateOption"
            role="option"
            @click="createAndAdd"
            class="flex items-center gap-2 px-3 py-1.5 cursor-pointer hover:bg-bg-elevated transition-colors text-accent-text"
            :class="{ 'bg-bg-elevated': filteredTags.length === highlightedIndex }"
          >
            <Plus class="w-3 h-3" aria-hidden="true" />
            <span class="text-[0.75rem]">Create "{{ searchQuery.trim() }}"</span>
          </div>
          <div v-if="filteredTags.length === 0 && !showCreateOption" class="px-3 py-3 text-center text-[0.75rem] text-text-tertiary">
            No tags found
          </div>
        </div>
      </div>
      </Transition>
    </Teleport>
  </div>
</template>
