<template>
  <Teleport to="body">
    <div v-if="open" class="fixed inset-0 z-[60] flex items-start justify-center p-4 pt-[10vh]" role="dialog" aria-modal="true">
      <div class="absolute inset-0 bg-black/60" @click="close" />
      <div class="relative flex max-h-[70vh] w-full max-w-lg flex-col overflow-hidden rounded-xl border border-zinc-700 bg-zinc-900 shadow-pop">
        <div class="flex items-center gap-2 border-b border-zinc-800 px-3">
          <Search :size="16" class="text-zinc-500" />
          <input
            ref="inputEl"
            v-model="query"
            type="text"
            class="w-full bg-transparent py-3 text-sm text-zinc-100 outline-none placeholder:text-zinc-600"
            :placeholder="t('palette.placeholder')"
            :aria-activedescendant="activeId ? `cmd-${activeId}` : undefined"
            role="combobox"
            aria-controls="cmd-listbox"
            aria-expanded="true"
            @keydown.down.prevent="move(1)"
            @keydown.up.prevent="move(-1)"
            @keydown.enter.prevent="run(results[activeIndex])"
            @keydown.esc.prevent="close"
          />
        </div>

        <ul v-if="results.length" id="cmd-listbox" role="listbox" class="min-h-0 flex-1 overflow-y-auto p-1.5">
          <li
            v-for="(item, i) in results"
            :id="`cmd-${item.id}`"
            :key="item.id"
            role="option"
            :aria-selected="i === activeIndex"
            class="flex cursor-pointer items-center gap-2 rounded-lg px-2.5 py-2"
            :class="i === activeIndex ? 'bg-zinc-800' : 'hover:bg-zinc-800/50'"
            @mousemove="activeIndex = i"
            @click="run(item)"
          >
            <component :is="item.icon" :size="15" class="shrink-0 text-zinc-500" />
            <span class="min-w-0 flex-1">
              <span class="block truncate text-sm text-zinc-100">{{ item.label }}</span>
              <span v-if="item.sublabel" class="block truncate font-mono text-[11px] text-zinc-500">{{ item.sublabel }}</span>
            </span>
            <span class="shrink-0 text-[10px] uppercase tracking-wide text-zinc-600">{{ t(`palette.types.${item.type}`) }}</span>
          </li>
        </ul>
        <p v-else class="px-3 py-6 text-center text-sm text-zinc-500">{{ t('palette.empty') }}</p>

        <div class="border-t border-zinc-800 px-3 py-1.5 text-[11px] text-zinc-600">{{ t('palette.hint') }}</div>
      </div>
    </div>
  </Teleport>
</template>

<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref, watch, type Component } from 'vue';
import { useRouter, type RouteLocationRaw } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { Search, GitBranch, TerminalSquare, Boxes, Plus, FolderPlus } from '@lucide/vue';
import { useSessionsStore } from '../stores/sessions';
import { useWorktreesStore } from '../stores/worktrees';
import { useGroupsStore } from '../stores/groups';
import { useCommandPalette } from '../composables/useCommandPalette';

interface PaletteItem {
  id: string;
  type: 'repo' | 'worktree' | 'session' | 'group' | 'action';
  label: string;
  sublabel?: string;
  icon: Component;
  keywords: string;
  to?: RouteLocationRaw;
  run?: () => void;
}

const { t } = useI18n();
const router = useRouter();
const { open, close, toggle } = useCommandPalette();
const sessions = useSessionsStore();
const worktrees = useWorktreesStore();
const groups = useGroupsStore();

const query = ref('');
const activeIndex = ref(0);
const inputEl = ref<HTMLInputElement | null>(null);

const basename = (p: string): string => p.split('/').filter(Boolean).pop() ?? p;

const items = computed<PaletteItem[]>(() => {
  const out: PaletteItem[] = [];
  for (const r of worktrees.repos) {
    out.push({ id: `repo-${r.id}`, type: 'repo', label: r.label, sublabel: r.path, icon: GitBranch as Component, keywords: `${r.label} ${r.path}`.toLowerCase(), to: { name: 'dashboard' } });
  }
  for (const w of worktrees.worktrees) {
    const target: RouteLocationRaw = w.sessions[0]
      ? { name: 'session', params: { id: w.sessions[0].id } }
      : { name: 'dashboard' };
    out.push({ id: `wt-${w.path}`, type: 'worktree', label: w.branch ?? basename(w.path), sublabel: w.path, icon: GitBranch as Component, keywords: `${w.branch ?? ''} ${w.path}`.toLowerCase(), to: target });
  }
  for (const s of sessions.sessions) {
    out.push({ id: `ses-${s.id}`, type: 'session', label: s.title ?? basename(s.cwd), sublabel: s.cwd, icon: TerminalSquare as Component, keywords: `${s.title ?? ''} ${s.cwd} ${s.command}`.toLowerCase(), to: { name: 'session', params: { id: s.id } } });
  }
  for (const g of groups.groups) {
    out.push({ id: `grp-${g.id}`, type: 'group', label: g.label, ...(g.description ? { sublabel: g.description } : {}), icon: Boxes as Component, keywords: `${g.label} ${g.description ?? ''}`.toLowerCase(), to: { name: 'group', params: { id: g.id } } });
  }
  // actions globales
  out.push({ id: 'act-newSession', type: 'action', label: t('palette.actions.newSession'), icon: Plus as Component, keywords: t('palette.actions.newSession').toLowerCase(), to: { name: 'sessions' } });
  out.push({ id: 'act-addRepo', type: 'action', label: t('palette.actions.addRepo'), icon: FolderPlus as Component, keywords: t('palette.actions.addRepo').toLowerCase(), to: { name: 'dashboard' } });
  out.push({ id: 'act-newGroup', type: 'action', label: t('palette.actions.newGroup'), icon: Boxes as Component, keywords: t('palette.actions.newGroup').toLowerCase(), to: { name: 'groups' } });
  return out;
});

// fuzzy minimal : tous les tokens présents (AND), score = somme des positions (plus tôt = mieux).
const results = computed<PaletteItem[]>(() => {
  const tokens = query.value.trim().toLowerCase().split(/\s+/).filter(Boolean);
  if (tokens.length === 0) return items.value.slice(0, 30);
  const scored: { item: PaletteItem; score: number }[] = [];
  for (const item of items.value) {
    let score = 0;
    let ok = true;
    for (const tok of tokens) {
      const idx = item.keywords.indexOf(tok);
      if (idx < 0) {
        ok = false;
        break;
      }
      score += idx;
    }
    if (ok) scored.push({ item, score });
  }
  return scored.sort((a, b) => a.score - b.score).slice(0, 30).map((s) => s.item);
});

watch(results, () => {
  if (activeIndex.value >= results.value.length) activeIndex.value = 0;
});

const activeId = computed(() => results.value[activeIndex.value]?.id);

function move(delta: number): void {
  const n = results.value.length;
  if (n === 0) return;
  activeIndex.value = (activeIndex.value + delta + n) % n;
}

function run(item: PaletteItem | undefined): void {
  if (!item) return;
  close();
  if (item.run) item.run();
  else if (item.to) void router.push(item.to);
}

watch(open, async (v) => {
  if (v) {
    query.value = '';
    activeIndex.value = 0;
    await nextTick();
    inputEl.value?.focus();
  }
});

// raccourci global ⌘K / Ctrl+K — en phase de capture pour passer devant xterm (SessionView).
function onKey(e: KeyboardEvent): void {
  if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
    e.preventDefault();
    toggle();
  }
}
onMounted(() => window.addEventListener('keydown', onKey, { capture: true }));
onUnmounted(() => window.removeEventListener('keydown', onKey, { capture: true }));
</script>
