<script setup lang="ts">
import { computed, onBeforeUnmount, ref, watch } from "vue";
import { useRouter } from "vue-router";

import { marketApi, type MarketEntity, type SearchGroup } from "../api/market";
import MarketPreviewPanel from "../market/MarketPreviewPanel.vue";
import { useUiStore } from "../stores/ui";

const router = useRouter();
const ui = useUiStore();
const query = ref("");
const groups = ref<SearchGroup[]>([]);
const loading = ref(false);
const error = ref("");
const activeIndex = ref(0);
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
let sequence = 0;
const flatItems = computed(() => groups.value.flatMap((group) => group.items));
const active = computed(() => flatItems.value[activeIndex.value] ?? null);

watch(query, (value) => {
  if (debounceTimer) clearTimeout(debounceTimer);
  const normalized = value.trim();
  if (!normalized) {
    groups.value = [];
    loading.value = false;
    error.value = "";
    return;
  }
  debounceTimer = setTimeout(() => void search(normalized), 160);
});

async function search(value: string): Promise<void> {
  const current = ++sequence;
  loading.value = true;
  error.value = "";
  try {
    const response = await marketApi.search(value);
    if (current === sequence) {
      groups.value = response.groups;
      activeIndex.value = 0;
    }
  } catch (reason) {
    if (current === sequence) error.value = reason instanceof Error ? reason.message : "搜索失败";
  } finally {
    if (current === sequence) loading.value = false;
  }
}

function select(item: MarketEntity): void {
  ui.closeDialog();
  void router.push({
    name: "entity-detail",
    params: { entityType: item.entity_type, identifier: item.identifier },
    query: { code: item.code, name: item.name, sector: item.sector ?? undefined },
  });
}

function keydown(event: KeyboardEvent): void {
  if (!flatItems.value.length) return;
  if (event.key === "ArrowDown" || event.key === "ArrowUp") {
    event.preventDefault();
    const offset = event.key === "ArrowDown" ? 1 : -1;
    activeIndex.value = (activeIndex.value + offset + flatItems.value.length) % flatItems.value.length;
  } else if (event.key === "Enter" && active.value) {
    event.preventDefault();
    select(active.value);
  }
}

function itemIndex(item: MarketEntity): number {
  return flatItems.value.findIndex((candidate) => candidate.identifier === item.identifier && candidate.entity_type === item.entity_type);
}

onBeforeUnmount(() => debounceTimer && clearTimeout(debounceTimer));
</script>

<template>
  <div class="search-panel" @keydown="keydown">
    <label class="sr-only" for="global-search">搜索股票、板块、题材或指数</label>
    <input id="global-search" v-model="query" class="input search-input" autofocus placeholder="搜索股票、板块、题材或指数" autocomplete="off" />
    <div class="search-layout">
      <div class="search-results" aria-live="polite">
        <div v-if="!query" class="search-empty"><p>输入代码或名称开始搜索</p><span>Ctrl+K</span></div>
        <div v-else-if="loading" class="search-empty"><p>正在搜索</p></div>
        <div v-else-if="error" class="search-empty"><p>{{ error }}</p></div>
        <div v-else-if="!flatItems.length" class="search-empty"><p>当前没有匹配结果</p></div>
        <template v-else>
          <section v-for="group in groups.filter((item) => item.items.length)" :key="group.entity_type" class="search-group">
            <h3>{{ group.label }}</h3>
            <button v-for="item in group.items" :key="item.identifier" type="button" class="search-result" :class="{ active: itemIndex(item) === activeIndex }" @mouseenter="activeIndex = itemIndex(item)" @focus="activeIndex = itemIndex(item)" @click="select(item)">
              <span class="result-code">{{ item.code }}</span><strong>{{ item.name }}</strong><span>{{ item.sector }}</span>
            </button>
          </section>
        </template>
      </div>
      <MarketPreviewPanel class="search-preview" :entity="active" />
    </div>
  </div>
</template>
