<script lang="ts">
  // Cmd/Ctrl+K quick-open over every note title — the fast path once you
  // already know roughly what you're looking for, as opposed to WikiTree's
  // browse-by-group path for when you don't. Built on the Dialog primitive
  // + a plain filtered list rather than shadcn-svelte's `command` component:
  // that component's interactive CLI installer couldn't be driven
  // non-interactively in this environment (it prompts to resolve overlapping
  // dependency files), and re-deriving the same arrow-key/Enter list nav by
  // hand here is a small amount of code for something this self-contained.
  import type { KnowledgeListItem } from '$lib/api'
  import * as Dialog from '$lib/components/ui/dialog'
  import { Input } from '$lib/components/ui/input'
  import StatusBadge from '$lib/components/StatusBadge.svelte'
  import { onDestroy } from 'svelte'

  let {
    open = $bindable(false),
    items,
    onSelect
  }: {
    open: boolean
    items: KnowledgeListItem[]
    onSelect: (slug: string) => void
  } = $props()

  let query = $state('')
  let activeIndex = $state(0)
  let inputEl = $state<HTMLInputElement | null>(null)

  const results = $derived.by(() => {
    const q = query.trim().toLowerCase()
    const pool = q
      ? items.filter(
          (it) =>
            it.title.toLowerCase().includes(q) ||
            it.slug.toLowerCase().includes(q) ||
            it.tags.some((t) => t.toLowerCase().includes(q))
        )
      : items
    return pool.slice(0, 30) // 102 notes total — cap the render, not the match
  })

  $effect(() => {
    void results // dependency only — re-run when the result set changes
    activeIndex = 0
  })

  // Reset on every open so quick-open never remembers the last search, and
  // focus the input once the dialog has actually mounted it.
  $effect(() => {
    if (open) {
      query = ''
      queueMicrotask(() => inputEl?.focus())
    }
  })

  function choose(slug: string): void {
    onSelect(slug)
    open = false
  }

  function handleKeydown(e: KeyboardEvent): void {
    if (!open) return
    if (e.key === 'ArrowDown') {
      e.preventDefault()
      activeIndex = Math.min(activeIndex + 1, results.length - 1)
    } else if (e.key === 'ArrowUp') {
      e.preventDefault()
      activeIndex = Math.max(activeIndex - 1, 0)
    } else if (e.key === 'Enter') {
      e.preventDefault()
      const hit = results[activeIndex]
      if (hit) choose(hit.slug)
    }
  }

  // A window-level listener rather than one on Dialog.Content: bits-ui's
  // Dialog renders its content through a portal with its own focus-trap
  // wiring, and an onkeydown prop passed straight through to Content did not
  // reliably receive ArrowDown/Enter in testing (focus landing inside the
  // trap didn't guarantee the event reached the element this component
  // attached the listener to). Capturing at the window and gating on `open`
  // sidesteps that entirely — Escape-to-close is still bits-ui's own
  // behavior, this only adds the list-navigation keys.
  window.addEventListener('keydown', handleKeydown)
  onDestroy(() => window.removeEventListener('keydown', handleKeydown))
</script>

<Dialog.Root bind:open>
  <Dialog.Content
    class="top-[20%] max-w-lg -translate-y-0 gap-0 p-0 sm:max-w-lg"
    showCloseButton={false}
  >
    <Input
      bind:ref={inputEl}
      bind:value={query}
      placeholder="Jump to a note…"
      class="h-11 rounded-b-none border-0 border-b px-3 text-sm focus-visible:ring-0"
    />
    <div class="max-h-80 overflow-y-auto p-1">
      {#each results as it, i (it.slug)}
        <button
          type="button"
          class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-sm {i ===
          activeIndex
            ? 'bg-primary/10 text-primary'
            : 'hover:bg-muted/50'}"
          onclick={() => choose(it.slug)}
          onmouseenter={() => (activeIndex = i)}
        >
          <span class="min-w-0 flex-1 truncate">{it.title}</span>
          <StatusBadge kind="type" value={it.kind} class="shrink-0 text-[9px]" />
        </button>
      {:else}
        <p class="py-6 text-center text-xs text-muted-foreground">No notes match "{query}".</p>
      {/each}
    </div>
  </Dialog.Content>
</Dialog.Root>
