<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from "vue";
import { useSearch } from "../../composables/useSearch";
import { useModal } from "../../composables/useModal";
import { useStackModal } from "../../composables/useStackModal";
import type { Template } from "../../types/template";
import type { Stack } from "../../types/stack";
import type { SearchableItem } from "../../composables/useSearch";

const props = defineProps<{
  templates: Template[];
  stacks?: Stack[];
}>();

const emit = defineEmits<{
  search: [query: string];
  select: [template: Template];
}>();

const { openModal } = useModal();
const { openStackModal } = useStackModal();
const { performSearch, results, clearSearch: clearSearchState } = useSearch();

// Local state
const query = ref("");
const inputRef = ref<HTMLInputElement | null>(null);
const isOpen = ref(false);
const selectedIndex = ref(-1);

// Debounce timer
let debounceTimer: ReturnType<typeof setTimeout>;

// Search when query changes
function handleInput() {
  isOpen.value = true;
  selectedIndex.value = -1;

  // Debounce 100ms
  clearTimeout(debounceTimer);
  debounceTimer = setTimeout(() => {
    performSearch(query.value, props.templates, props.stacks || []);
    emit("search", query.value);
  }, 100);
}

// Computed suggestions from search results (full results with match data)
const suggestions = computed(() => results.value.slice(0, 8));

// Group results by type (Templates vs Stacks)
const groupedSuggestions = computed(() => {
  const templates = suggestions.value.filter(
    (r) => r.item.itemType === "template",
  );
  const stacks = suggestions.value.filter((r) => r.item.itemType === "stack");
  return { templates, stacks };
});

// Show dropdown when there are suggestions
const showDropdown = computed(
  () => isOpen.value && suggestions.value.length > 0,
);

// Show empty state when query exists but no results
const showEmptyState = computed(
  () =>
    isOpen.value && query.value.length >= 2 && suggestions.value.length === 0,
);

// Screen reader announcement text
const announceText = computed(() => {
  if (showEmptyState.value) {
    return `No results for ${query.value}`;
  }
  if (suggestions.value.length > 0) {
    return `${suggestions.value.length} result${suggestions.value.length !== 1 ? "s" : ""} found`;
  }
  return "";
});

// Icon mapping
const typeIcons: Record<string, string> = {
  agent: "ph-robot",
  command: "ph-terminal",
  mcp: "ph-plug",
  setting: "ph-gear",
  hook: "ph-webhooks-logo",
  skill: "ph-magic-wand",
  stack: "ph-stack",
};

// Select suggestion - open modal
function selectSuggestion(item: SearchableItem) {
  query.value = "";
  isOpen.value = false;
  clearSearchState();

  if (item.itemType === "template") {
    openModal(item as Template);
    emit("select", item as Template);
  } else if (item.itemType === "stack") {
    openStackModal(item as Stack);
    emit("select", item as unknown as Template); // Close search modal
  }
}

// Clear search
function clearSearch() {
  query.value = "";
  isOpen.value = false;
  selectedIndex.value = -1;
  clearSearchState();
  emit("search", "");
  inputRef.value?.focus();
}

// Set query from empty state suggestion
function setQuery(newQuery: string) {
  query.value = newQuery;
  isOpen.value = true;
  handleInput();
}

// Keyboard navigation
function handleKeydown(e: KeyboardEvent) {
  if (!showDropdown.value) return;

  switch (e.key) {
    case "ArrowDown":
      e.preventDefault();
      selectedIndex.value = Math.min(
        selectedIndex.value + 1,
        suggestions.value.length - 1,
      );
      break;
    case "ArrowUp":
      e.preventDefault();
      selectedIndex.value = Math.max(selectedIndex.value - 1, -1);
      break;
    case "Enter":
      e.preventDefault();
      if (selectedIndex.value >= 0) {
        selectSuggestion(suggestions.value[selectedIndex.value]);
      }
      break;
    case "Escape":
      isOpen.value = false;
      selectedIndex.value = -1;
      break;
  }
}

// "/" to focus (local shortcut when component is mounted)
function handleGlobalKeydown(e: KeyboardEvent) {
  const isInputFocused =
    document.activeElement?.tagName === "INPUT" ||
    document.activeElement?.tagName === "TEXTAREA";

  // "/" shortcut
  if (e.key === "/" && !isInputFocused) {
    e.preventDefault();
    inputRef.value?.focus();
  }
}

// Close dropdown on click outside
function handleClickOutside(e: MouseEvent) {
  const target = e.target as HTMLElement;
  if (!target.closest(".search-container")) {
    isOpen.value = false;
  }
}

// Highlight matched terms in search results
function highlightMatches(
  text: string,
  match: Record<string, string[]>,
): string {
  if (!match || Object.keys(match).length === 0) return text;

  // Get all matched terms from MiniSearch result
  const matchedTerms = new Set<string>();
  Object.values(match).forEach((terms) => {
    terms.forEach((term) => matchedTerms.add(term.toLowerCase()));
  });

  if (matchedTerms.size === 0) return text;

  // Escape regex special chars
  const escapeRegex = (str: string) =>
    str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");

  // Create regex pattern for all matched terms
  const pattern = Array.from(matchedTerms).map(escapeRegex).join("|");
  const regex = new RegExp(`(${pattern})`, "gi");

  // Replace matched terms with highlighted spans
  return text.replace(
    regex,
    '<span class="font-semibold text-warm-accent">$1</span>',
  );
}

onMounted(() => {
  document.addEventListener("keydown", handleGlobalKeydown);
  document.addEventListener("click", handleClickOutside);
});

onUnmounted(() => {
  document.removeEventListener("keydown", handleGlobalKeydown);
  document.removeEventListener("click", handleClickOutside);
  clearTimeout(debounceTimer);
});
</script>

<template>
  <div
    class="search-container relative w-full"
    role="combobox"
    :aria-expanded="showDropdown || showEmptyState"
    aria-haspopup="listbox"
    aria-owns="search-results"
  >
    <!-- Search Icon -->
    <i
      class="ph ph-magnifying-glass absolute left-4 top-1/2 -translate-y-1/2 text-warm-text-tertiary text-lg pointer-events-none z-10"
    ></i>

    <!-- Input -->
    <input
      ref="inputRef"
      v-model="query"
      type="text"
      placeholder="Search templates and stacks..."
      class="input-warm pl-12 pr-20 w-full focus:ring-2 focus:ring-warm-accent/50 focus:outline-none"
      autocomplete="off"
      aria-label="Search templates and stacks"
      aria-autocomplete="list"
      aria-controls="search-results"
      :aria-activedescendant="
        selectedIndex >= 0 ? `result-${selectedIndex}` : undefined
      "
      @input="handleInput"
      @focus="isOpen = true"
      @keydown="handleKeydown"
    />

    <!-- Live region for screen readers -->
    <div class="sr-only" aria-live="polite" aria-atomic="true">
      {{ announceText }}
    </div>

    <!-- Shortcut Hint & Clear Button -->
    <div
      class="absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-2 z-10"
    >
      <button
        v-if="query.length > 0"
        class="icon-btn"
        @click="clearSearch"
        aria-label="Clear search"
      >
        <i class="ph ph-x text-sm"></i>
      </button>
      <kbd
        v-else
        class="hidden sm:inline-flex items-center gap-1 px-2 py-1 rounded-lg bg-warm-bg-elevated border border-warm-border-subtle text-warm-text-tertiary text-xs font-mono"
      >
        <span class="text-[10px]">⌘K</span>
      </kbd>
    </div>

    <!-- Suggestions Dropdown -->
    <Transition name="dropdown">
      <div
        v-if="showDropdown || showEmptyState"
        id="search-results"
        class="absolute top-full left-0 right-0 mt-2 bg-warm-bg-surface border border-warm-border-default rounded-warm shadow-warm-lg overflow-hidden z-50"
      >
        <!-- Empty State -->
        <div v-if="showEmptyState" class="px-4 py-8 text-center" role="status">
          <i
            class="ph ph-magnifying-glass text-warm-text-muted text-3xl mb-3 block"
          ></i>
          <p class="text-warm-text-secondary text-sm mb-1">
            No results for "<strong class="text-warm-text-primary">{{
              query
            }}</strong
            >"
          </p>
          <p class="text-warm-text-tertiary text-xs mt-3">
            Try:
            <button
              @click="setQuery('docker')"
              class="text-warm-accent hover:underline focus:underline focus:outline-none ml-1"
            >
              docker</button
            >,
            <button
              @click="setQuery('nextjs')"
              class="text-warm-accent hover:underline focus:underline focus:outline-none"
            >
              nextjs</button
            >, or
            <button
              @click="setQuery('security')"
              class="text-warm-accent hover:underline focus:underline focus:outline-none"
            >
              security
            </button>
          </p>
        </div>

        <!-- Results -->
        <div
          v-if="showDropdown"
          class="max-h-[60vh] overflow-y-auto"
          role="listbox"
        >
          <!-- Templates Section -->
          <div v-if="groupedSuggestions.templates.length > 0">
            <div
              v-if="groupedSuggestions.stacks.length > 0"
              class="px-4 py-2 text-xs font-semibold text-warm-text-tertiary uppercase tracking-wide bg-warm-bg-deep/50"
            >
              Templates
            </div>
            <ul class="py-1">
              <li
                v-for="(result, index) in groupedSuggestions.templates"
                :key="result.item.id"
                :id="`result-${index}`"
                role="option"
                :aria-selected="index === selectedIndex"
                class="flex items-center gap-3 px-4 py-3 cursor-pointer transition-colors min-h-[44px]"
                :class="[
                  index === selectedIndex
                    ? 'bg-warm-accent/10 text-warm-text-primary'
                    : 'hover:bg-warm-bg-elevated text-warm-text-secondary hover:text-warm-text-primary',
                ]"
                @click="selectSuggestion(result.item)"
                @mouseenter="selectedIndex = index"
              >
                <div
                  class="w-8 h-8 rounded-lg bg-warm-bg-elevated flex items-center justify-center flex-shrink-0"
                >
                  <i
                    :class="[
                      'ph',
                      typeIcons[result.item.type || result.item.category] ||
                        'ph-package',
                      'text-warm-accent',
                    ]"
                  ></i>
                </div>
                <div class="flex-1 min-w-0">
                  <div
                    class="font-mono text-sm truncate"
                    v-html="highlightMatches(result.item.name, result.match)"
                  ></div>
                  <div class="text-xs text-warm-text-tertiary line-clamp-2">
                    {{ result.item.description }}
                  </div>
                </div>
                <span
                  :class="[
                    'badge-' +
                      (result.item.type === 'mcp'
                        ? 'mcp'
                        : result.item.type || result.item.category),
                    'text-2xs',
                  ]"
                >
                  {{ result.item.type }}
                </span>
              </li>
            </ul>
          </div>

          <!-- Stacks Section -->
          <div v-if="groupedSuggestions.stacks.length > 0">
            <div
              v-if="groupedSuggestions.templates.length > 0"
              class="px-4 py-2 text-xs font-semibold text-warm-text-tertiary uppercase tracking-wide bg-warm-bg-deep/50 border-t border-warm-border-subtle"
            >
              Stacks
            </div>
            <ul class="py-1">
              <li
                v-for="(result, index) in groupedSuggestions.stacks"
                :key="result.item.id"
                :id="`result-${index + groupedSuggestions.templates.length}`"
                role="option"
                :aria-selected="
                  index + groupedSuggestions.templates.length === selectedIndex
                "
                class="flex items-center gap-3 px-4 py-3 cursor-pointer transition-colors min-h-[44px]"
                :class="[
                  index + groupedSuggestions.templates.length === selectedIndex
                    ? 'bg-warm-accent/10 text-warm-text-primary'
                    : 'hover:bg-warm-bg-elevated text-warm-text-secondary hover:text-warm-text-primary',
                ]"
                @click="selectSuggestion(result.item)"
                @mouseenter="
                  selectedIndex = index + groupedSuggestions.templates.length
                "
              >
                <div
                  class="w-8 h-8 rounded-lg bg-warm-bg-elevated flex items-center justify-center flex-shrink-0"
                >
                  <i class="ph ph-stack text-warm-accent"></i>
                </div>
                <div class="flex-1 min-w-0">
                  <div
                    class="font-mono text-sm truncate"
                    v-html="highlightMatches(result.item.name, result.match)"
                  ></div>
                  <div class="text-xs text-warm-text-tertiary line-clamp-2">
                    {{ result.item.description }}
                  </div>
                </div>
                <span class="badge-stack text-2xs"> stack </span>
              </li>
            </ul>
          </div>
        </div>

        <!-- Results count -->
        <div
          class="px-4 py-2 border-t border-warm-border-subtle text-xs text-warm-text-tertiary"
        >
          {{ suggestions.length }} result{{
            suggestions.length !== 1 ? "s" : ""
          }}
          <span v-if="templates.length > suggestions.length">
            of {{ templates.length }}</span
          >
        </div>
      </div>
    </Transition>
  </div>
</template>

<style scoped>
.dropdown-enter-active,
.dropdown-leave-active {
  transition: all 0.15s ease;
}

.dropdown-enter-from,
.dropdown-leave-to {
  opacity: 0;
  transform: translateY(-8px);
}
</style>
