<script setup lang="ts" generic="TData, TValue">
import { Link } from '@inertiajs/vue3';
import type { ColumnDef } from '@tanstack/vue-table';
import { FlexRender, getCoreRowModel, useVueTable } from '@tanstack/vue-table';
import { computed, provide } from 'vue';
import DataTableToolbar from '@/components/data-table/DataTableToolbar.vue';
import { Button } from '@/components/ui/button';
import {
    Empty, EmptyDescription,
    EmptyHeader, EmptyTitle
} from '@/components/ui/empty';
import {
    Table,
    TableBody,
    TableCell,
    TableHead,
    TableHeader,
    TableRow,
} from '@/components/ui/table';
import TableEmpty from '@/components/ui/table/TableEmpty.vue';
import type {
    DataTableFilterDef,
    DataTablePagination,
    DataTablePaginationLink,
    DataTableSort,
} from '@/types/data-table';

const props = withDefaults(
    defineProps<{
        columns: ColumnDef<TData, TValue>[];
        data: TData[];
        pagination?: DataTablePagination;
        sort?: DataTableSort | null;
        filterDefs?: DataTableFilterDef[];
        filterValues?: Record<string, string>;
        searchPlaceholder?: string;
        paginationLinks?: DataTablePaginationLink[];
    }>(),
    {
        searchPlaceholder: 'Ketikkan sesuatu...',
    }
);

const search = defineModel<string>('search', { default: '' });

const emit = defineEmits<{
    'sort-change': [sort: DataTableSort | null];
    'filter-change': [key: string, value: string];
    'filters-reset': [];
}>();

const rowNumberColumn: ColumnDef<TData> = {
    id: '_row_number',
    header: 'No.',
    enableSorting: false,
    enableHiding: false,
    cell: ({ row }) => {
        const offset = props.pagination
            ? (props.pagination.currentPage - 1) * props.pagination.perPage
            : 0;

        return offset + row.index + 1;
    },
};

const resolvedColumns = computed(() => [rowNumberColumn, ...props.columns]);

const table = useVueTable({
    get data() {
        return props.data;
    },
    get columns() {
        return resolvedColumns.value;
    },
    getCoreRowModel: getCoreRowModel(),
    manualSorting: true,
});

function handleSort(column: string): void {
    const current = props.sort;

    if (current?.column !== column) {
        emit('sort-change', { column, direction: 'asc' });

        return;
    }

    if (current.direction === 'asc') {
        emit('sort-change', { column, direction: 'desc' });

        return;
    }

    emit('sort-change', null);
}

provide('data-table-sort', {
    sort: () => props.sort,
    onSort: handleSort,
});

const showingCount = computed(() => props.data.length);

const paginationSummary = computed(() => {
    if (!props.pagination) {
        return null;
    }

    const { total } = props.pagination;

    if (total === 0) {
        return 'Menampilkan 0 data';
    }

    return `Menampilkan ${showingCount.value} data dari ${total}`;
});
</script>

<template>
    <div class="space-y-4">
        <DataTableToolbar v-model:search="search" :filter-defs="filterDefs" :filter-values="filterValues"
            :search-placeholder="searchPlaceholder" @filter-change="(key, value) => emit('filter-change', key, value)"
            @filters-reset="emit('filters-reset')" />

        <div class="min-w-0 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">
                            <FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.header"
                                :props="header.getContext()" />
                        </TableHead>
                    </TableRow>
                </TableHeader>
                <TableBody>
                    <template v-if="table.getRowModel().rows.length">
                        <TableRow v-for="row in table.getRowModel().rows" :key="row.id"
                            :data-state="row.getIsSelected() ? 'selected' : undefined">
                            <TableCell v-for="cell in row.getVisibleCells()" :key="cell.id">
                                <FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
                            </TableCell>
                        </TableRow>
                    </template>
                    <TableEmpty v-else :colspan="resolvedColumns.length">
                        <Empty>
                            <EmptyHeader>
                                <EmptyTitle>Data tidak ditemukan</EmptyTitle>
                                <EmptyDescription>
                                    Silakan lakukan pencarian atau filter untuk menemukan data yang Anda cari.
                                </EmptyDescription>
                            </EmptyHeader>
                        </Empty>
                    </TableEmpty>
                </TableBody>
            </Table>
        </div>

        <div
            v-if="pagination"
            class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"
        >
            <p class="text-muted-foreground text-sm">
                {{ paginationSummary }}
            </p>

            <div
                v-if="paginationLinks?.length && pagination.lastPage > 1"
                class="flex flex-wrap items-center justify-center gap-1 sm:justify-end"
            >
                <Button
                    v-for="link in paginationLinks"
                    :key="`${link.label}-${link.url}`"
                    variant="outline"
                    size="sm"
                    :disabled="!link.url || link.active"
                    as-child
                >
                    <Link v-if="link.url" :href="link.url" preserve-scroll>
                        <span v-html="link.label" />
                    </Link>
                    <span v-else v-html="link.label" />
                </Button>
            </div>
        </div>
    </div>
</template>
