<script setup lang="ts" generic="TData, TValue">
import type {
  ColumnDef,
  ColumnFiltersState,
  ExpandedState,
  SortingState,
} from '@tanstack/vue-table'
import {
  FlexRender,
  getCoreRowModel,
  getExpandedRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  useVueTable,
} from '@tanstack/vue-table'
import { ArrowUpDown } from '@lucide/vue'
import { computed, h, ref } from 'vue'

import { Button } from '@/components/ui/button'
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from '@/components/ui/table'
import { valueUpdater } from '@/lib/utils'
import { Skeleton } from '@/components/ui/skeleton'
import DataTablePagination from './DataTablePagination.vue'
import {
  Empty, EmptyDescription,
  EmptyHeader, EmptyTitle
} from '@/components/ui/empty'

const props = defineProps<{
  columns: ColumnDef<TData, TValue>[]
  data: TData[]
  loading?: boolean
}>()

const sorting = ref<SortingState>([])
const columnFilters = ref<ColumnFiltersState>([])
const expanded = ref<ExpandedState>([])

const numberColumn: ColumnDef<TData> = {
  id: 'number',
  accessorFn: (_, index) => index,
  header: ({ column }) => {
    return h(Button, {
      variant: 'ghost',
      class: 'justify-center',
      onClick: () => column.toggleSorting(column.getIsSorted() === 'asc'),
    }, () => ['#', h(ArrowUpDown, { class: 'ml-2 h-4 w-4' })])
  },
  cell: ({ row }) => {
    return h('div', { class: 'text-center' }, `${row.index + 1}`)
  },
  enableHiding: false,
}

const allColumns = computed<ColumnDef<TData>[]>(() => [
  numberColumn,
  ...props.columns,
])

const table = useVueTable({
  get data() { return props.data },
  get columns() { return allColumns.value },
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
  getSortedRowModel: getSortedRowModel(),
  getPaginationRowModel: getPaginationRowModel(),
  getExpandedRowModel: getExpandedRowModel(),
  onSortingChange: updaterOrValue => valueUpdater(updaterOrValue, sorting),
  onColumnFiltersChange: updaterOrValue => valueUpdater(updaterOrValue, columnFilters),
  onExpandedChange: updaterOrValue => valueUpdater(updaterOrValue, expanded),
  state: {
    get sorting() { return sorting.value },
    get columnFilters() { return columnFilters.value },
    get expanded() { return expanded.value },
  },
})

function skeletonWidth(columnId: string) {
  switch (columnId) {
    case 'number': return 'w-6'
    case 'actions': return 'w-8'
    default: return 'w-full'
  }
}

defineExpose({ table })
</script>

<template>
  <div class="w-full">
    <slot name="filters" :table="table" />
    <div class="rounded-md border">
      <Table>
        <TableHeader>
          <TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
            <TableHead v-for="header in headerGroup.headers" :key="header.id"
              :class="['actions', 'number'].includes(header.column.id) ? 'w-0' : ''">
              <FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.header"
                :props="header.getContext()" />
            </TableHead>
          </TableRow>
        </TableHeader>
        <TableBody>
          <template v-if="loading">
            <TableRow v-for="i in 5" :key="i">
              <TableCell v-for="col in allColumns" :key="col.id"
                :class="['actions', 'number'].includes(col.id) ? 'w-0' : ''">
                <Skeleton class="h-5 w-full" :class="skeletonWidth(col.id)" />
              </TableCell>
            </TableRow>
          </template>
          <template v-else-if="table.getRowModel().rows?.length">
            <template v-for="row in table.getRowModel().rows" :key="row.id">
              <TableRow>
                <TableCell v-for="cell in row.getVisibleCells()" :key="cell.id"
                  :class="['actions', 'number'].includes(cell.column.id) ? 'w-0' : ''">
                  <FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
                </TableCell>
              </TableRow>
              <TableRow v-if="row.getIsExpanded()">
                <TableCell :colspan="row.getAllCells().length">
                  {{ JSON.stringify(row.original) }}
                </TableCell>
              </TableRow>
            </template>
          </template>

          <TableRow v-else>
            <TableCell :colspan="allColumns.length" class="h-24 text-center">
              <Empty class="from-muted/50 to-background h-full bg-linear-to-b from-30%">
                <EmptyHeader>
                  <EmptyTitle>Tidak ada data ditemukan</EmptyTitle>
                  <EmptyDescription>
                    Sesuaikan filter atau kata kunci pencarian, lalu coba lagi.
                  </EmptyDescription>
                </EmptyHeader>
              </Empty>
            </TableCell>
          </TableRow>
        </TableBody>
      </Table>
    </div>

    <DataTablePagination v-if="!loading" :table="table" />
  </div>
</template>
