<script lang="ts">
  import { onMount } from 'svelte'
  import { fetchEntities, type Entity } from '$lib/api'
  import { liveEvents, subscribeEvents } from '$lib/stores/events'
  import EntityTable from '$lib/components/EntityTable.svelte'
  import EntityGraph, { type GraphInfo } from '$lib/components/EntityGraph.svelte'
  import EntityDetailContent from '$lib/components/EntityDetailContent.svelte'
  import MultiSelectFilter from '$lib/components/MultiSelectFilter.svelte'
  import { categories, filtersForCategory, type Category } from '$lib/categories'
  import { Button } from '$lib/components/ui/button'
  import { Input } from '$lib/components/ui/input'
  import * as Select from '$lib/components/ui/select'
  import NetworkIcon from '@lucide/svelte/icons/share-2'
  import TableIcon from '@lucide/svelte/icons/table-2'
  import LocateFixedIcon from '@lucide/svelte/icons/locate-fixed'
  import XIcon from '@lucide/svelte/icons/x'

  type View = 'graph' | 'table'

  function loadView(): View {
    if (typeof localStorage === 'undefined') return 'graph'
    return localStorage.getItem('oikos-kb-view') === 'table' ? 'table' : 'graph'
  }

  let category = $state<Category>('fleet')
  let view = $state<View>(loadView())
  let selectedSlug = $state<string | null>(null)

  function setView(v: View) {
    view = v
    if (typeof localStorage !== 'undefined') localStorage.setItem('oikos-kb-view', v)
  }

  function select(slug: string | null) {
    selectedSlug = slug
  }

  // ─── table data: fetched here (not inside EntityTable) so the search/type
  // toolbar lives in the shared page toolbar instead of the resizable browse
  // pane, where its width is at the mercy of the divider and it would
  // truncate. This also keeps the browse pane header-free, so it and the
  // detail pane both start flush under the toolbar and end up the same height.
  let tableEntities = $state<Entity[]>([])
  let tableLoading = $state(true)
  let query = $state('')
  let typeFilter = $state('all')

  async function loadTable() {
    tableLoading = true
    const filterSets = filtersForCategory(category)
    const results = await Promise.all(filterSets.map((f) => fetchEntities(f)))
    tableEntities = results.flat()
    tableLoading = false
  }

  onMount(() => {
    const unsubscribe = subscribeEvents()
    return unsubscribe
  })

  $effect(() => {
    if (view !== 'table') return
    category
    loadTable()
  })

  $effect(() => {
    const ev = $liveEvents[0]
    if (view !== 'table' || !ev || !ev.type.startsWith('entity.')) return
    loadTable()
  })

  const tableTypes = $derived(Array.from(new Set(tableEntities.map((e) => e.type))).sort())
  const filteredEntities = $derived.by(() => {
    const q = query.trim().toLowerCase()
    return tableEntities.filter((e) => {
      if (typeFilter !== 'all' && e.type !== typeFilter) return false
      if (q && !e.slug.toLowerCase().includes(q) && !e.name.toLowerCase().includes(q)) return false
      return true
    })
  })

  // ─── graph controls: same reasoning as the table toolbar above — these
  // live here instead of inside EntityGraph so they render at full toolbar
  // width instead of being squeezed by the resizable browse pane.
  let graphRoot = $state('')
  let graphDepth = $state(2)
  let graphSearch = $state('')
  let graphReloadToken = $state(0)
  let graphResetToken = $state(0)
  let graphActiveNodeTypes = $state<Set<string>>(new Set())
  let graphActiveRelTypes = $state<Set<string>>(new Set())
  let graphInfo = $state<GraphInfo>({ allNodeTypes: [], allRelTypes: [], relColors: new Map(), visibleCount: 0, truncated: false, zoomPct: 100 })

  function commitGraphQuery() {
    graphReloadToken++
  }

  function resetGraph() {
    graphRoot = ''
    graphSearch = ''
    graphResetToken++
  }

  function relColorFor(type: string): string {
    return graphInfo.relColors.get(type) ?? '#30363d'
  }

  // ─── resizable browse/detail split (pattern from Chat.svelte) ─────────
  const DETAIL_MIN = 320
  const DETAIL_MAX = 900
  function loadDetailWidth(): number {
    if (typeof localStorage === 'undefined') return 420
    const v = Number(localStorage.getItem('oikos-kb-detail-width'))
    return v >= DETAIL_MIN && v <= DETAIL_MAX ? v : 420
  }
  let detailWidth = $state(loadDetailWidth())
  let resizing = $state(false)

  function startResize(e: PointerEvent) {
    e.preventDefault()
    resizing = true
    ;(e.currentTarget as Element).setPointerCapture(e.pointerId)
    const startX = e.clientX
    const startW = detailWidth
    function move(ev: PointerEvent) {
      detailWidth = Math.min(DETAIL_MAX, Math.max(DETAIL_MIN, startW + (startX - ev.clientX)))
    }
    function up() {
      resizing = false
      if (typeof localStorage !== 'undefined') localStorage.setItem('oikos-kb-detail-width', String(detailWidth))
      window.removeEventListener('pointermove', move)
      window.removeEventListener('pointerup', up)
    }
    window.addEventListener('pointermove', move)
    window.addEventListener('pointerup', up)
  }
</script>

<div class="flex h-full flex-col gap-3 p-4">
  <!-- toolbar: category perspective + view toggle -->
  <div class="flex flex-wrap items-center gap-3">
    <div class="inline-flex overflow-hidden rounded-md border">
      {#each categories as c}
        <Button
          variant={category === c.id ? 'secondary' : 'ghost'}
          size="sm"
          class="h-8 rounded-none border-0"
          onclick={() => (category = c.id)}
        >
          {c.label}
        </Button>
      {/each}
    </div>

    <div class="ml-auto inline-flex overflow-hidden rounded-md border">
      <Button
        variant={view === 'graph' ? 'secondary' : 'ghost'}
        size="sm"
        class="h-8 rounded-none border-0"
        onclick={() => setView('graph')}
      >
        <NetworkIcon class="mr-1 size-3.5" /> Graph
      </Button>
      <Button
        variant={view === 'table' ? 'secondary' : 'ghost'}
        size="sm"
        class="h-8 rounded-none border-0"
        onclick={() => setView('table')}
      >
        <TableIcon class="mr-1 size-3.5" /> Table
      </Button>
    </div>
  </div>

  {#if view === 'table'}
    <div class="flex flex-wrap items-center gap-2">
      <Input placeholder="Filter by slug or name…" bind:value={query} class="h-8 max-w-xs text-xs" />
      <Select.Root type="single" bind:value={typeFilter}>
        <Select.Trigger class="h-8 w-40 text-xs">
          {typeFilter === 'all' ? 'All types' : typeFilter}
        </Select.Trigger>
        <Select.Content>
          <Select.Item value="all">All types</Select.Item>
          {#each tableTypes as type}
            <Select.Item value={type}>{type}</Select.Item>
          {/each}
        </Select.Content>
      </Select.Root>
      <span class="text-xs text-muted-foreground">{filteredEntities.length} of {tableEntities.length}</span>
    </div>
  {:else}
    <div class="flex flex-wrap items-center gap-2">
      <Input placeholder="Root entity…" bind:value={graphRoot} class="h-8 max-w-44 text-xs" onchange={commitGraphQuery} />
      <Input type="number" min="1" max="5" bind:value={graphDepth} class="h-8 w-16 text-xs" onchange={commitGraphQuery} />
      <Input placeholder="Search / highlight…" bind:value={graphSearch} class="h-8 max-w-44 text-xs" />
      <Button variant="outline" size="sm" class="h-8" onclick={resetGraph}>
        <LocateFixedIcon class="mr-1 size-3.5" />
        Reset
      </Button>
      <MultiSelectFilter label="Nodes" options={graphInfo.allNodeTypes} bind:selected={graphActiveNodeTypes} />
      <MultiSelectFilter label="Edges" options={graphInfo.allRelTypes} bind:selected={graphActiveRelTypes} colorFor={relColorFor} />
      <span class="ml-auto text-xs text-muted-foreground">
        {graphInfo.visibleCount} nodes{graphInfo.truncated ? ' · truncated' : ''} · {graphInfo.zoomPct}%
      </span>
    </div>
  {/if}

  <!-- resizable browse | detail split -->
  <div class="flex min-h-0 flex-1">
    <div class="flex min-w-0 flex-1 flex-col">
      {#if view === 'graph'}
        <EntityGraph
          {category}
          {selectedSlug}
          onSelect={select}
          bind:root={graphRoot}
          depth={graphDepth}
          search={graphSearch}
          reloadToken={graphReloadToken}
          resetToken={graphResetToken}
          bind:activeNodeTypes={graphActiveNodeTypes}
          bind:activeRelTypes={graphActiveRelTypes}
          bind:info={graphInfo}
        />
      {:else}
        <EntityTable entities={filteredEntities} loading={tableLoading} {selectedSlug} onSelect={select} />
      {/if}
    </div>

    <button
      type="button"
      class="group/rz relative w-1.5 shrink-0 cursor-col-resize touch-none"
      onpointerdown={startResize}
      aria-label="Resize detail panel"
    >
      <span
        class="absolute inset-y-0 left-1/2 w-px -translate-x-1/2 transition-colors {resizing
          ? 'bg-primary/60'
          : 'bg-border group-hover/rz:bg-primary/50'}"
      ></span>
    </button>

    <div class="flex shrink-0 flex-col overflow-hidden rounded-lg border" style="width: {detailWidth}px">
      {#if selectedSlug}
        <div class="flex shrink-0 items-center justify-end border-b p-1">
          <Button variant="ghost" size="icon" class="size-7" onclick={() => select(null)} aria-label="Close detail panel">
            <XIcon class="size-4" />
          </Button>
        </div>
        <div class="min-h-0 flex-1">
          {#key selectedSlug}
            <EntityDetailContent slug={selectedSlug} onSelectEntity={select} />
          {/key}
        </div>
      {:else}
        <div class="flex h-full items-center justify-center p-6 text-center text-sm text-muted-foreground">
          Select an entity to see its detail.
        </div>
      {/if}
    </div>
  </div>
</div>
