<script lang="ts">
  import type { Entity } from '$lib/api'
  import * as Table from '$lib/components/ui/table'
  import { Badge } from '$lib/components/ui/badge'
  import { Skeleton } from '$lib/components/ui/skeleton'
  import SortHeader from '$lib/components/data-table/SortHeader.svelte'
  import EmptyState from '$lib/components/EmptyState.svelte'
  import HealthDotRenderer from '$lib/components/data-table/renderers/HealthDotRenderer.svelte'
  import StatusBadgeRenderer from '$lib/components/data-table/renderers/StatusBadgeRenderer.svelte'
  import ChevronRightIcon from '@lucide/svelte/icons/chevron-right'
  import ChevronDownIcon from '@lucide/svelte/icons/chevron-down'

  let {
    entities,
    loading,
    selectedSlug = null,
    onSelect,
    childToParent = null
  }: {
    entities: Entity[]
    loading: boolean
    selectedSlug?: string | null
    onSelect: (slug: string) => void
    childToParent?: Map<string, string> | null
  } = $props()

  type SortKey = 'slug' | 'type' | 'name' | 'state' | 'health'
  let sortKey = $state<SortKey>('slug')
  let sortDir = $state<'asc' | 'desc'>('asc')
  let collapsedNodes = $state<Set<string>>(new Set())

  function toggleNode(slug: string, e: Event) {
    e.stopPropagation()
    const next = new Set(collapsedNodes)
    if (next.has(slug)) next.delete(slug)
    else next.add(slug)
    collapsedNodes = next
  }

  function sortBy(key: SortKey) {
    if (sortKey === key) {
      sortDir = sortDir === 'asc' ? 'desc' : 'asc'
    } else {
      sortKey = key
      sortDir = 'asc'
    }
  }

  function getSortState(key: SortKey) {
    if (sortKey !== key) return { sorted: false, direction: 'asc' as const }
    return { sorted: true, direction: sortDir }
  }

  const healthRank: Record<string, number> = {
    down: 0,
    degraded: 1,
    stale: 2,
    unknown: 3,
    healthy: 4
  }

  function sortValue(entity: Entity, key: SortKey): string | number {
    if (key === 'health') return entity.health ? (healthRank[entity.health] ?? -1) : -1
    return (entity[key as keyof Entity] ?? '').toString().toLowerCase()
  }

  const sortedEntities = $derived.by(() => {
    const sorted = [...entities].sort((a, b) => {
      const av = sortValue(a, sortKey)
      const bv = sortValue(b, sortKey)
      if (av < bv) return -1
      if (av > bv) return 1
      return 0
    })
    if (sortDir === 'desc') sorted.reverse()
    return sorted
  })

  const childrenByParent = $derived.by(() => {
    const map = new Map<string, Entity[]>()
    if (!childToParent) return map
    const visibleSlugs = new Set(entities.map((e) => e.slug))
    for (const e of sortedEntities) {
      const parentSlug = childToParent.get(e.slug)
      if (parentSlug && visibleSlugs.has(parentSlug)) {
        if (!map.has(parentSlug)) map.set(parentSlug, [])
        map.get(parentSlug)!.push(e)
      }
    }
    return map
  })

  const nestedSlugs = $derived.by(() => {
    const set = new Set<string>()
    for (const children of childrenByParent.values()) for (const c of children) set.add(c.slug)
    return set
  })

  const topLevelEntities = $derived.by(() =>
    childToParent ? sortedEntities.filter((e) => !nestedSlugs.has(e.slug)) : sortedEntities
  )

  const skeletonSlugWidths = ['w-24', 'w-20', 'w-28', 'w-16', 'w-24', 'w-20', 'w-28', 'w-16']
  const skeletonNameWidths = ['w-32', 'w-40', 'w-24', 'w-36', 'w-28', 'w-40', 'w-24', 'w-32']
</script>

{#if loading}
  <div class="h-full min-h-0 overflow-hidden rounded-md border">
    <Table.Root>
      <Table.Header>
        <Table.Row>
          <Table.Head>Slug</Table.Head>
          <Table.Head>Type</Table.Head>
          <Table.Head>Name</Table.Head>
          <Table.Head>State</Table.Head>
          <Table.Head>Health</Table.Head>
        </Table.Row>
      </Table.Header>
      <Table.Body>
        {#each skeletonSlugWidths as slugWidth, i}
          <Table.Row class="hover:bg-transparent">
            <Table.Cell><Skeleton class="h-4 {slugWidth}" /></Table.Cell>
            <Table.Cell><Skeleton class="h-5 w-16 rounded-full" /></Table.Cell>
            <Table.Cell><Skeleton class="h-4 {skeletonNameWidths[i]}" /></Table.Cell>
            <Table.Cell><Skeleton class="h-5 w-14 rounded-full" /></Table.Cell>
            <Table.Cell>
              <div class="flex items-center gap-1.5">
                <Skeleton class="size-2 shrink-0 rounded-full" />
                <Skeleton class="h-4 w-12" />
              </div>
            </Table.Cell>
          </Table.Row>
        {/each}
      </Table.Body>
    </Table.Root>
  </div>
{:else}
  {#snippet row(entity: Entity, level: number, ancestors: Set<string>)}
    {@const ancestorsWithSelf = new Set(ancestors).add(entity.slug)}
    {@const children = (childrenByParent.get(entity.slug) ?? []).filter(
      (c) => !ancestorsWithSelf.has(c.slug)
    )}
    <Table.Row
      class="cursor-pointer {entity.slug === selectedSlug ? 'bg-muted' : ''}"
      role="row"
      aria-level={level}
      aria-expanded={children.length > 0 ? !collapsedNodes.has(entity.slug) : undefined}
      tabindex={0}
      onclick={() => onSelect(entity.slug)}
      onkeydown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault()
          onSelect(entity.slug)
        }
      }}
    >
      <Table.Cell class="font-mono text-xs">
        <span class="flex items-center gap-1" style="padding-left: {(level - 1) * 1.25}rem">
          <span class="inline-flex size-3.5 shrink-0 items-center justify-center">
            {#if children.length > 0}
              <button
                type="button"
                class="rounded text-muted-foreground hover:text-foreground"
                onclick={(e) => toggleNode(entity.slug, e)}
                aria-label={collapsedNodes.has(entity.slug)
                  ? `Expand ${entity.slug}`
                  : `Collapse ${entity.slug}`}
              >
                {#if collapsedNodes.has(entity.slug)}
                  <ChevronRightIcon class="size-3.5" />
                {:else}
                  <ChevronDownIcon class="size-3.5" />
                {/if}
              </button>
            {/if}
          </span>
          {entity.slug}
          {#if children.length > 0}
            <span class="text-muted-foreground">({children.length})</span>
          {/if}
        </span>
      </Table.Cell>
      <Table.Cell><Badge variant="outline">{entity.type}</Badge></Table.Cell>
      <Table.Cell>{entity.name}</Table.Cell>
      <Table.Cell>
        <StatusBadgeRenderer value={entity.state ?? ''} kind="state" />
      </Table.Cell>
      <Table.Cell>
        <HealthDotRenderer row={entity} value={null} />
      </Table.Cell>
    </Table.Row>
    {#if children.length > 0 && !collapsedNodes.has(entity.slug)}
      {#each children as child (child.id)}
        {@render row(child, level + 1, ancestorsWithSelf)}
      {/each}
    {/if}
  {/snippet}
  <div class="h-full min-h-0 overflow-auto rounded-md border">
    <Table.Root role={childToParent ? 'treegrid' : undefined}>
      <Table.Header>
        <Table.Row>
          {@const ssSlug = getSortState('slug')}
          <Table.Head>
            <SortHeader
              label="Slug"
              sorted={ssSlug.sorted}
              direction={ssSlug.direction}
              onclick={() => sortBy('slug')}
            />
          </Table.Head>
          {@const ssType = getSortState('type')}
          <Table.Head>
            <SortHeader
              label="Type"
              sorted={ssType.sorted}
              direction={ssType.direction}
              onclick={() => sortBy('type')}
            />
          </Table.Head>
          {@const ssName = getSortState('name')}
          <Table.Head>
            <SortHeader
              label="Name"
              sorted={ssName.sorted}
              direction={ssName.direction}
              onclick={() => sortBy('name')}
            />
          </Table.Head>
          {@const ssState = getSortState('state')}
          <Table.Head>
            <SortHeader
              label="State"
              sorted={ssState.sorted}
              direction={ssState.direction}
              onclick={() => sortBy('state')}
            />
          </Table.Head>
          {@const ssHealth = getSortState('health')}
          <Table.Head>
            <SortHeader
              label="Health"
              sorted={ssHealth.sorted}
              direction={ssHealth.direction}
              onclick={() => sortBy('health')}
            />
          </Table.Head>
        </Table.Row>
      </Table.Header>
      <Table.Body>
        {#each topLevelEntities as entity (entity.id)}
          {@render row(entity, 1, new Set())}
        {:else}
          <EmptyState message="No entities in this layer match the filter." colspan={5} />
        {/each}
      </Table.Body>
    </Table.Root>
  </div>
{/if}
