<script lang="ts">
  // The Knowledge app, redesigned as a wiki: browse, read, and now actually
  // edit everything the operator and Nomos have recorded — 102 notes that
  // were previously stats-only (the old page could search and show a
  // recency feed, but nothing was clickable: see git history on this file).
  //
  // Three panes (WikiTree | WikiReader | WikiContextRail) for browsing and
  // editing day to day, plus a separate Cleanup mode (WikiCleanup) for the
  // maintenance work the collection actually needs — duplicate pileups,
  // tag-casing drift, orphaned notes, and the trash. Both modes share the
  // same `items` list, loaded once here.
  import { onMount } from 'svelte'
  import { Pane, Splitpanes } from 'svelte-splitpanes'
  import { listKnowledge, KnowledgeApiError, type KnowledgeListItem } from '$lib/api'
  import WikiTree from '$lib/components/knowledge/WikiTree.svelte'
  import WikiReader from '$lib/components/knowledge/WikiReader.svelte'
  import WikiContextRail from '$lib/components/knowledge/WikiContextRail.svelte'
  import WikiCleanup from '$lib/components/knowledge/WikiCleanup.svelte'
  import WikiNewDialog from '$lib/components/knowledge/WikiNewDialog.svelte'
  import WikiQuickOpen from '$lib/components/knowledge/WikiQuickOpen.svelte'
  import * as Dialog from '$lib/components/ui/dialog'
  import { Button } from '$lib/components/ui/button'
  import { Skeleton } from '$lib/components/ui/skeleton'
  import WrenchIcon from '@lucide/svelte/icons/wrench'
  import BookOpenIcon from '@lucide/svelte/icons/book-open'

  type Mode = 'wiki' | 'cleanup'

  let items = $state<KnowledgeListItem[]>([])
  let itemsLoading = $state(true)
  let selectedSlug = $state<string | null>(null)
  let mode = $state<Mode>('wiki')
  let newDialogOpen = $state(false)
  let quickOpenOpen = $state(false)
  // True while WikiReader has an in-progress, unsaved edit — set via
  // bind:dirty. requestSelect below is the single choke point every
  // selection path (tree, context rail, quick-open, a newly-created note)
  // goes through, so gating it here is enough to stop a stray click from
  // silently discarding an edit in progress (see WikiReader's own comment
  // on why this is "in edit mode" rather than a real dirty-diff).
  let readerDirty = $state(false)
  let pendingSlug = $state<string | null>(null)
  // Distinct from "0 notes": listKnowledge now throws on a failed request
  // rather than returning [] (a real outage was previously indistinguishable
  // from an empty collection — see api.ts's comment on the same helper).
  let loadError = $state('')

  async function loadItems(): Promise<void> {
    itemsLoading = true
    loadError = ''
    try {
      items = await listKnowledge()
    } catch (e) {
      loadError = e instanceof KnowledgeApiError ? e.message : 'Failed to load notes.'
    } finally {
      itemsLoading = false
    }
  }

  // Cmd/Ctrl+K quick-open, scoped to while this app's window is around —
  // Desktop.svelte doesn't have a global command-palette convention to hook
  // into, so this is a plain window listener added/removed with the
  // component's lifetime rather than a shell-level keybinding.
  function handleKeydown(e: KeyboardEvent): void {
    if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
      e.preventDefault()
      quickOpenOpen = true
    }
  }

  onMount(() => {
    loadItems()
    window.addEventListener('keydown', handleKeydown)
    return () => window.removeEventListener('keydown', handleKeydown)
  })

  // Every note's own slug, for WikiReader's click handler to tell a
  // wiki-internal link (navigate in place) from an entity link (open an
  // entity window) — see wikiText.ts's slugFromKbHref.
  const knownSlugs = $derived(new Set(items.map((i) => i.slug)))
  const selectedItem = $derived(items.find((i) => i.slug === selectedSlug) ?? null)

  function selectNow(slug: string): void {
    readerDirty = false
    selectedSlug = slug
    mode = 'wiki'
  }

  // Every selection path (tree click, context-rail click, quick-open,
  // opening a just-created note) routes through here rather than mutating
  // selectedSlug directly, so an in-progress edit can never be silently
  // discarded by a stray click elsewhere in the app.
  function requestSelect(slug: string): void {
    if (slug === selectedSlug) {
      mode = 'wiki' // already selected — e.g. Cleanup's "view" links back into Wiki mode
      return
    }
    if (readerDirty) {
      pendingSlug = slug
      return
    }
    selectNow(slug)
  }

  function confirmDiscardAndSwitch(): void {
    if (pendingSlug) selectNow(pendingSlug)
    pendingSlug = null
  }

  function handleCreated(slug: string): void {
    loadItems()
    requestSelect(slug)
  }
</script>

<div class="flex h-full flex-col gap-2 p-2">
  <div class="flex items-center justify-between">
    <h1 class="text-lg font-semibold">Knowledge</h1>
    <div class="flex items-center gap-2">
      <span class="text-xs text-muted-foreground">{items.length} notes</span>
      <button
        type="button"
        class="rounded border px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground hover:bg-muted/50"
        onclick={() => (quickOpenOpen = true)}
      >
        ⌘K
      </button>
      <div class="inline-flex overflow-hidden rounded-md border">
        <button
          type="button"
          class="flex items-center gap-1 px-2 py-1 text-xs {mode === 'wiki'
            ? 'bg-secondary text-secondary-foreground'
            : 'hover:bg-muted/50'}"
          onclick={() => (mode = 'wiki')}
        >
          <BookOpenIcon class="size-3.5" /> Wiki
        </button>
        <button
          type="button"
          class="flex items-center gap-1 px-2 py-1 text-xs {mode === 'cleanup'
            ? 'bg-secondary text-secondary-foreground'
            : 'hover:bg-muted/50'}"
          onclick={() => (mode = 'cleanup')}
        >
          <WrenchIcon class="size-3.5" /> Cleanup
        </button>
      </div>
    </div>
  </div>

  <div class="min-h-0 flex-1">
    {#if itemsLoading}
      <!-- Shaped like the real three-pane layout below (same Splitpanes
           proportions) rather than a centered spinner: a spinner tells the
           operator nothing about what's about to render and costs a full
           reflow the instant real content lands. This costs none — the
           panes are already the right size. -->
      <Splitpanes theme="oikos-theme" dblClickSplitter={false} class="h-full">
        <Pane size={22} minSize={15} maxSize={40}>
          <div class="flex h-full flex-col gap-3 overflow-hidden p-1.5">
            <Skeleton class="h-7 w-full" />
            <Skeleton class="h-3 w-16" />
            <div class="flex flex-col gap-3">
              {#each Array(4) as _, gi (gi)}
                <div class="flex flex-col gap-1.5">
                  <Skeleton class="h-3 w-20" />
                  <div class="ml-3 flex flex-col gap-2 border-l pl-2">
                    {#each Array(3) as _, ri (ri)}
                      <Skeleton class="h-3.5" style="width: {75 - ri * 15}%" />
                    {/each}
                  </div>
                </div>
              {/each}
            </div>
          </div>
        </Pane>
        <Pane size={56} minSize={30}>
          <div class="flex h-full flex-col gap-3 overflow-hidden p-2">
            <Skeleton class="h-4 w-32" />
            <Skeleton class="h-9 w-40" />
            <div class="mt-1 grid grid-cols-4 gap-px overflow-hidden rounded-lg bg-border/60">
              {#each Array(4) as _, i (i)}
                <div class="flex flex-col gap-1.5 bg-card px-3 py-2.5">
                  <Skeleton class="h-3 w-14" />
                  <Skeleton class="h-5 w-8" />
                </div>
              {/each}
            </div>
            <Skeleton class="mt-2 h-3 w-full" />
            <div class="mt-2 flex flex-col gap-2.5">
              {#each Array(5) as _, i (i)}
                <Skeleton class="h-4" style="width: {85 - i * 8}%" />
              {/each}
            </div>
          </div>
        </Pane>
      </Splitpanes>
    {:else if loadError}
      <div class="flex h-full flex-col items-center justify-center gap-2 text-sm">
        <p class="text-destructive">{loadError}</p>
        <Button size="sm" variant="outline" onclick={loadItems}>Retry</Button>
      </div>
    {:else if mode === 'wiki'}
      <Splitpanes theme="oikos-theme" dblClickSplitter={false} class="h-full">
        <Pane size={22} minSize={15} maxSize={40}>
          <div class="h-full overflow-hidden p-1.5">
            <WikiTree
              {items}
              {selectedSlug}
              onSelect={requestSelect}
              onNew={() => (newDialogOpen = true)}
            />
          </div>
        </Pane>
        <Pane size={56} minSize={30}>
          <div class="h-full overflow-hidden p-2">
            <WikiReader
              item={selectedItem}
              allItems={items}
              {knownSlugs}
              onNavigate={requestSelect}
              onNew={() => (newDialogOpen = true)}
              onChanged={loadItems}
              bind:dirty={readerDirty}
            />
          </div>
        </Pane>
        <!-- The context rail is about the selected note, so it only exists
             when there is one. Left mounted it rendered a "Nothing selected."
             placeholder next to the overview — two competing empty states,
             and a fifth of the width spent saying nothing. -->
        {#if selectedItem}
          <Pane size={22} minSize={15} maxSize={40}>
            <div class="h-full overflow-hidden p-1.5">
              <WikiContextRail item={selectedItem} allItems={items} onSelect={requestSelect} />
            </div>
          </Pane>
        {/if}
      </Splitpanes>
    {:else}
      <WikiCleanup onSelect={requestSelect} onChanged={loadItems} />
    {/if}
  </div>
</div>

<WikiNewDialog bind:open={newDialogOpen} onCreated={handleCreated} />
<WikiQuickOpen bind:open={quickOpenOpen} {items} onSelect={requestSelect} />

<Dialog.Root
  open={pendingSlug !== null}
  onOpenChange={(o) => {
    if (!o) pendingSlug = null
  }}
>
  <Dialog.Content class="sm:max-w-sm">
    <Dialog.Header>
      <Dialog.Title>Discard unsaved changes?</Dialog.Title>
      <Dialog.Description>
        You're editing a note. Switching now will discard what you haven't saved.
      </Dialog.Description>
    </Dialog.Header>
    <Dialog.Footer>
      <Button variant="ghost" onclick={() => (pendingSlug = null)}>Keep editing</Button>
      <Button variant="destructive" onclick={confirmDiscardAndSwitch}>Discard and switch</Button>
    </Dialog.Footer>
  </Dialog.Content>
</Dialog.Root>
