<script setup lang="ts">
import { useDebounce } from '@vueuse/core';

const props = withDefaults(defineProps<{
    endpoint: '/api/admin/users';
    query?: Record<string, unknown>;
    columns: number;
    perPage?: number;
    marked?: (el: unknown) => boolean;
    fixed?: boolean;
    count?: boolean;
}>(), {
    query: () => ({}),
    perPage: 100,
});

const debouncedQuery = useDebounce(toRef(props, 'query'));

const page = ref(0);

const tableData = useFetch(props.endpoint, {
    params: computed(() => ({
        ...debouncedQuery.value,
        limit: props.perPage,
        offset: page.value * props.perPage,
    })),
});

const isLoading = computed(() => {
    return JSON.stringify(props.query) !== JSON.stringify(debouncedQuery.value) || tableData.status.value === 'pending';
});

const dataPage = computed(() => tableData.data.value?.data);
const rowsCount = computed(() => tableData.data.value?.count);

const pages = computed(() => {
    if (rowsCount.value === undefined) {
        return 0;
    }
    return Math.ceil(rowsCount.value / props.perPage);
});

type VPage = {
    page: number;
    text: string | number;
    enabled: boolean;
} | {
    page?: never;
    text: string;
    enabled: false;
};

const pagesRange = computed(() => {
    const vPages: VPage[] = [];
    vPages.push({ page: 0, text: '«', enabled: page.value > 0 });
    vPages.push({ page: page.value - 1, text: '‹', enabled: page.value > 0 });
    for (let i = 0; i < pages.value; i++) {
        if (i <= 4 || (page.value - 3 <= i && i <= page.value + 3) || i >= pages.value - 3) {
            vPages.push({ page: i, text: i + 1, enabled: true });
        } else if (vPages[vPages.length - 1].text !== '…') {
            vPages.push({ text: '…', enabled: false });
        }
    }
    vPages.push({ page: page.value + 1, text: '›', enabled: page.value < pages.value - 1 });
    vPages.push({ page: pages.value - 1, text: '»', enabled: page.value < pages.value - 1 });
    return vPages;
});

watch(() => props.endpoint, () => {
    page.value = 0;
});
watch(() => props.query, (after, before) => {
    if (JSON.stringify(after) === JSON.stringify(before)) {
        return;
    }
    page.value = 0;
});

const reset = () => {
    page.value = 0;
};

const thead = useTemplateRef('thead');
const focus = () => {
    setTimeout((_) => {
        thead.value?.scrollIntoView();
    }, 300);
};

defineExpose({ reset, focus });
</script>

<template>
    <section class="table-responsive">
        <div class="input-group ">
            <span class="input-group-text">
                <Spinner v-if="isLoading" size="1.25em" />
                <Icon v-else v="filter" />
            </span>
            <slot name="filter"></slot>
        </div>

        <table :class="['table table-striped table-hover', fixed ? `table-fixed-${columns}` : '']">
            <thead ref="thead">
                <tr>
                    <td :colspan="columns">
                        <nav v-if="pages > 1">
                            <ul class="pagination pagination-sm justify-content-center mb-0">
                                <li v-for="p in pagesRange" :class="['page-item', p.page === page ? 'active' : '', p.enabled ? '' : 'disabled']">
                                    <a v-if="p.enabled" class="page-link" href="#" @click.prevent="page = p.page">
                                        {{ p.text }}
                                    </a>
                                    <span v-else class="page-link">
                                        {{ p.text }}
                                    </span>
                                </li>
                            </ul>
                        </nav>
                    </td>
                </tr>
                <tr v-if="count && rowsCount">
                    <td :colspan="columns">
                        <T>table.count</T><T>quotation.colon</T>
                        <strong>{{ rowsCount }}</strong>
                    </td>
                </tr>
                <tr>
                    <slot name="header"></slot>
                </tr>
            </thead>
            <tbody>
                <template v-if="dataPage !== undefined">
                    <template v-if="rowsCount">
                        <tr v-for="el in dataPage" :key="el.id" :class="{ marked: marked ? marked(el) : false }">
                            <slot name="row" :el="el"></slot>
                        </tr>
                    </template>
                    <template v-else>
                        <tr>
                            <td :colspan="columns" class="text-center">
                                <slot name="empty">
                                    <Icon v="search" />
                                    <T>table.empty</T>
                                </slot>
                            </td>
                        </tr>
                    </template>
                </template>
            </tbody>
            <tfoot>
                <tr>
                    <td :colspan="columns + 1">
                        <nav v-if="pages > 1">
                            <ul class="pagination pagination-sm justify-content-center">
                                <li v-for="p in pagesRange" :class="['page-item', p.page === page ? 'active' : '', p.enabled ? '' : 'disabled']">
                                    <a v-if="p.enabled" class="page-link" href="#" @click.prevent="page = p.page">
                                        {{ p.text }}
                                    </a>
                                    <span v-else class="page-link">
                                        {{ p.text }}
                                    </span>
                                </li>
                            </ul>
                        </nav>
                    </td>
                </tr>
            </tfoot>
        </table>
    </section>
</template>

<style lang="scss">
    @import "assets/variables";

    .marked {
        border-inline-start: 3px solid $primary;
    }
</style>
