<script lang="ts">
  import { TableHandler } from '@vincjo/datatables'
  import { Skeleton } from '$lib/components/ui/skeleton'
  import SortHeader from './SortHeader.svelte'
  import Toolbar from './Toolbar.svelte'
  import Pagination from './pagination/Pagination.svelte'
  import EmptyState from '$lib/components/EmptyState.svelte'
  import BadgeRenderer from './renderers/BadgeRenderer.svelte'
  import HealthDotRenderer from './renderers/HealthDotRenderer.svelte'
  import RelativeTimeRenderer from './renderers/RelativeTimeRenderer.svelte'
  import DateRenderer from './renderers/DateRenderer.svelte'
  import StatusBadgeRenderer from './renderers/StatusBadgeRenderer.svelte'
  import { resolveCellValue } from './columns'
  import type { DataTableColumn, BuiltinRenderer } from './types'

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  type Row = Record<string, any>

  const renderers: Record<string, unknown> = {
    badge: BadgeRenderer,
    'health-dot': HealthDotRenderer,
    'relative-time': RelativeTimeRenderer,
    date: DateRenderer,
    'status-badge': StatusBadgeRenderer
  }

  let {
    columns,
    data = [],
    pageSize = 20,
    paginated = false,
    searchable = false,
    bordered = true,
    loading = false,
    emptyMessage = 'No items.',
    selected = $bindable(null),
    onRowClick = undefined,
    class: className,
    children
  }: {
    columns: DataTableColumn<Row>[]
    data: Row[]
    pageSize?: number
    paginated?: boolean
    searchable?: boolean
    bordered?: boolean
    loading?: boolean
    emptyMessage?: string
    selected?: string | null
    onRowClick?: (row: Row) => void
    class?: string
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    children?: any
  } = $props()

  const table = new TableHandler([], { pageSize: 20 })

  // One SortBuilder per sortable column — each tracks its own direction/isActive
  // via $derived runes internally.
  const sortBuilders = new Map<string, ReturnType<typeof table.createSort>>()

  function getSortBuilder(col: DataTableColumn<Row>) {
    if (!sortBuilders.has(col.key)) {
      sortBuilders.set(col.key, table.createSort(col.accessor ?? col.key))
    }
    return sortBuilders.get(col.key)!
  }

  let search = $state.raw(
    table.createSearch({
      filterFunction: (row: Row, q: string) => {
        if (!q) return true
        const lower = q.toLowerCase()
        for (const col of columns) {
          if (col.hidden) continue
          const val = String(resolveCellValue(row, col) ?? '').toLowerCase()
          if (val.includes(lower)) return true
        }
        return false
      }
    })
  )

  $effect(() => {
    table.setRowsPerPage(pageSize)
  })

  $effect(() => {
    table.setRows(data)
  })

  function handleSearch(q: string) {
    search.set(q)
    if (paginated) table.setPage(1)
  }

  function colAlignClass(col: DataTableColumn<Row>): string {
    if (col.align === 'right') return 'text-right'
    if (col.align === 'center') return 'text-center'
    return ''
  }

  function colTruncateClass(col: DataTableColumn<Row>): string {
    return col.truncate ? 'min-w-0 overflow-hidden text-ellipsis' : ''
  }

  function colStyle(col: DataTableColumn<Row>): string | undefined {
    if (!col.width) return undefined
    const w = typeof col.width === 'number' ? col.width + 'px' : col.width
    return `width: ${w}; min-width: ${w}`
  }

  const visibleCols = $derived(columns.filter((c) => !c.hidden))
  const rows = $derived(table.rows as Row[])

  const skeletonWidths = ['w-24', 'w-20', 'w-28', 'w-16', 'w-24', 'w-20', 'w-28', 'w-16']
</script>

<div class={['flex flex-col h-full min-h-0', className].filter(Boolean).join(' ')}>
  <Toolbar {table} {searchable} {paginated} onSearchChange={handleSearch} {children} />

  <div
    class={['flex flex-col min-h-0 flex-1', bordered ? 'rounded-xl border' : '']
      .filter(Boolean)
      .join(' ')}
  >
    <table class="w-full caption-bottom text-sm table-fixed">
      <thead class="[&_tr]:border-b">
        <tr>
          {#each visibleCols as col (col.key)}
            <th
              class={[
                'text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap',
                'bg-card/95',
                col.headerClass,
                colAlignClass(col)
              ]
                .filter(Boolean)
                .join(' ')}
              style={colStyle(col)}
            >
              {#if col.sortable !== false}
                {@const sb = getSortBuilder(col)}
                <SortHeader
                  label={col.header}
                  sorted={sb.isActive}
                  direction={sb.direction ?? 'asc'}
                  onclick={() => sb.set()}
                />
              {:else}
                {col.header}
              {/if}
            </th>
          {/each}
        </tr>
      </thead>
    </table>
    <div class="min-h-0 flex-1 overflow-y-auto">
      <table class="w-full caption-bottom text-sm table-fixed">
        <tbody class="[&_tr:last-child]:border-0">
          {#if loading}
            {#each skeletonWidths as w, i}
              <tr class="border-b transition-colors hover:bg-transparent">
                {#each visibleCols as col (col.key)}
                  <td
                    class={[col.class, colAlignClass(col), colTruncateClass(col)]
                      .filter(Boolean)
                      .join(' ')}
                    style={colStyle(col)}
                  >
                    <Skeleton
                      class="h-4 {skeletonWidths[
                        (i + visibleCols.indexOf(col)) % skeletonWidths.length
                      ]}"
                    />
                  </td>
                {/each}
              </tr>
            {/each}
          {:else if rows.length === 0}
            <EmptyState message={emptyMessage} colspan={visibleCols.length} />
          {:else}
            {#each rows as row, idx (row.id ?? row.slug ?? `row-${idx}`)}
              <tr
                class={[
                  'border-b transition-colors hover:bg-muted/50',
                  onRowClick ? 'cursor-pointer' : '',
                  selected === (row.id ?? row.slug) ? 'bg-muted' : ''
                ]
                  .filter(Boolean)
                  .join(' ')}
                tabindex={onRowClick ? 0 : undefined}
                onclick={onRowClick ? () => onRowClick(row) : undefined}
                onkeydown={onRowClick
                  ? (e: KeyboardEvent) => {
                      if (e.key === 'Enter' || e.key === ' ') {
                        e.preventDefault()
                        onRowClick(row)
                      }
                    }
                  : undefined}
              >
                {#each visibleCols as col (col.key)}
                  {@const val = resolveCellValue(row, col)}
                  <td
                    class={[
                      'p-2 align-middle whitespace-nowrap',
                      col.class,
                      colAlignClass(col),
                      colTruncateClass(col)
                    ]
                      .filter(Boolean)
                      .join(' ')}
                    style={colStyle(col)}
                  >
                    {#if typeof col.render === 'string'}
                      {@const R = renderers[col.render]}
                      {#if R}
                        <!-- eslint-disable-next-line @typescript-eslint/no-explicit-any -->
                        <R value={val} {row} {...col.renderProps ?? {}} />
                      {:else}
                        {String(val ?? '—')}
                      {/if}
                    {:else if typeof col.render === 'function'}
                      <col.render {row} value={val} {...col.renderProps ?? {}} />
                    {:else}
                      {String(val ?? '—')}
                    {/if}
                  </td>
                {/each}
              </tr>
            {/each}
          {/if}
        </tbody>
      </table>
    </div>
  </div>

  {#if paginated}
    <Pagination {table} />
  {/if}
</div>
