<script setup lang="ts">
import { toRef, type PropType, computed, ref, watch, onMounted, defineProps, defineEmits } from 'vue';
import { twMerge } from 'tailwind-merge';
import type { PTAttribs } from './types';

const props = defineProps({
    pt: {
        type: Object,
        default: function () {}
    },
    items: {
        type: Object,
        required: true
    },
    itemsPerPage: {
        type: Number,
        default: 10,
        validator: (value: number) => value > 0 || 'itemsPerPage must be greater than 0.'
    },
    currentPage: {
        type: Number,
        default: 10
    },
    modelValue: {
        type: Number,
        required: true,
        validator: (value: number) => value > 0 || 'v-model must be greater than 0.'
    },
    maxPagesShown: {
        type: Number,
        default: 5,
        validator: (value: number) => value > 0 || 'maxPagesShown must be greater than 0.'
    },
    dir: {
        type: String as PropType<'ltr' | 'rtl'>,
        default: 'ltr',
        validator: (value: string) => ['ltr', 'rtl'].includes(value) || 'dir must be either "ltr" or "rtl".'
    },
    type: {
        type: String as PropType<'link' | 'button'>,
        default: 'button',
        validator: (value: string) => ['link', 'button'].includes(value) || 'type must be "link" or "button".'
    },
    onClick: Function,
    locale: {
        type: String as PropType<'en' | 'ar' | 'ir'>,
        default: 'en',
        validator: (value: string) => ['en', 'ar', 'ir'].includes(value) || 'locale must be "en", "ar", or "ir".'
    },
    prevButtonContent: {
        type: String,
        default: '<'
    },
    nextButtonContent: {
        type: String,
        default: '>'
    },
    hidePrevNext: {
        type: Boolean,
        default: false
    },
    hidePrevNextWhenEnds: {
        type: Boolean,
        default: false
    },
    showBreakpointButtons: {
        type: Boolean,
        default: true
    },
    disableBreakpointButtons: {
        type: Boolean,
        default: false
    },
    startingBreakpointContent: {
        type: String,
        default: '...'
    },
    endingBreakpointButtonContent: {
        type: String,
        default: '...'
    },
    showJumpButtons: {
        type: Boolean,
        default: false
    },
    linkUrl: {
        type: String,
        default: '#'
    },
    backwardJumpButtonContent: {
        type: String,
        default: '<<'
    },
    forwardJumpButtonContent: {
        type: String,
        default: '>>'
    },
    disablePagination: {
        type: Boolean,
        default: false
    },
    showEndingButtons: {
        type: Boolean,
        default: false
    },
    firstPageContent: {
        type: String,
        default: 'First'
    },
    lastPageContent: {
        type: String,
        default: 'Last'
    },

    // Class props
    backButtonClass: String,
    nextButtonClass: {
        type: String,
        default: 'next-button'
    },
    firstButtonClass: {
        type: String,
        default: 'first-button'
    },
    lastButtonClass: {
        type: String,
        default: 'last-button'
    },
    numberButtonsClass: {
        type: String,
        default: 'number-buttons'
    },
    startingBreakpointButtonClass: {
        type: String,
        default: 'starting-breakpoint-button'
    },
    endingBreakPointButtonClass: {
        type: String,
        default: 'ending-breakpoint-button'
    },
    firstPageButtonClass: {
        type: String,
        default: 'first-page-button'
    },
    lastPageButtonClass: {
        type: String,
        default: 'last-page-button'
    },
    paginateButtonsClass: {
        type: String,
        default: 'px-3 py-1 rounded-full font-[1000] text-sm'
    },
    disabledPaginateButtonsClass: {
        type: String,
        default: 'disabled-paginate-buttons'
    },
    disabledBreakPointButtonClass: String,
    backwardJumpButtonClass: String,
    forwardJumpButtonClass: String,
    disabledBackwardJumpButtonClass: String,
    disabledBackButtonClass: String,
    disabledFirstButtonClass: String,
    disabledLastButtonClass: String,
    disabledNextButtonClass: String,
    disabledForwardJumpButtonClass: String
});

const classes = ref({
    root: { class: `` },
    paginator: { class: `` },
    activePaginator: { class: '' }
} as PTAttribs);

const rootClasses = computed(() => twMerge(classes.value.root?.class, props.pt?.root?.class));
const paginatorClasses = computed(() => twMerge(classes.value.paginator?.class, props.pt?.paginator?.class));
const activePaginator = computed(() => twMerge(classes.value.activePaginator?.class, props.pt?.activePaginator?.class));

if (props.currentPage && !props.modelValue) {
    throw new Error('currentPage/current-page is deprecated, use v-model instead to set the current page.');
}

if (!props.modelValue) {
    throw new TypeError('v-model is required for the paginate component.');
}

const currentPageRef = toRef(props, 'modelValue');
const emit = defineEmits(['update:modelValue', 'click', 'update:paginatedItems']);

const onClickHandler = (number: number) => {
    if (number === currentPageRef.value || number > totalPages.value || number < 1 || props.disablePagination) return;
    emit('update:modelValue', number);
    emit('click', number);
};

const NumbersLocale = (number: number) => {
    switch (props.locale) {
        case 'ar':
            return number.toLocaleString('ar-SA');
        case 'ir':
            return number.toLocaleString('fa-IR');
        default:
            return number;
    }
};

const navigationHandler = (page: number) => {
    if (props.type !== 'link') return '';
    return props.linkUrl.replace('[page]', page.toString());
};

const totalPages = computed(() => Math.ceil(props.items.length / props.itemsPerPage));

const paginatedItems = computed(() => {
    const start = (currentPageRef.value - 1) * props.itemsPerPage;
    const end = start + props.itemsPerPage;
    return props.items.slice(start, end);
});

watch(currentPageRef, emitPaginatedItems);
onMounted(emitPaginatedItems);

function emitPaginatedItems() {
    emit('update:paginatedItems', paginatedItems.value);
}

const paginate = computed(() => {
    let startPage: number, endPage: number;
    if (totalPages.value <= props.maxPagesShown) {
        startPage = 1;
        endPage = totalPages.value;
    } else {
        const maxPagesShownBeforeCurrentPage = Math.floor(props.maxPagesShown / 2);
        const maxPagesShownAfterCurrentPage = Math.ceil(props.maxPagesShown / 2) - 1;
        if (currentPageRef.value <= maxPagesShownBeforeCurrentPage) {
            startPage = 1;
            endPage = props.maxPagesShown;
        } else if (currentPageRef.value + maxPagesShownAfterCurrentPage >= totalPages.value) {
            startPage = totalPages.value - props.maxPagesShown + 1;
            endPage = totalPages.value;
        } else {
            startPage = currentPageRef.value - maxPagesShownBeforeCurrentPage;
            endPage = currentPageRef.value + maxPagesShownAfterCurrentPage;
        }
    }
    let pages = Array.from(Array(endPage + 1 - startPage).keys()).map((i) => startPage + i);

    if (props.dir === 'rtl') {
        pages = pages.reverse();
    }

    return { currentPage: currentPageRef.value, itemsPerPage: props.itemsPerPage, totalPages: totalPages, startPage, endPage, pages };
});

const isRtl = computed(() => props.dir === 'rtl');
const backButtonIfCondition = computed(() =>
    isRtl.value ? !props.hidePrevNextWhenEnds || currentPageRef.value !== totalPages.value : !props.hidePrevNextWhenEnds || currentPageRef.value !== 1
);
const nextButtonIfCondition = computed(() =>
    isRtl.value ? !props.hidePrevNextWhenEnds || currentPageRef.value !== 1 : !props.hidePrevNextWhenEnds || currentPageRef.value !== totalPages.value
);
const startingBreakPointButtonIfCondition = computed(() => (isRtl.value ? paginate.value.pages[0] < totalPages.value - 1 : paginate.value.pages[0] >= 3));
const endingBreakPointButtonIfCondition = computed(() =>
    isRtl.value ? paginate.value.pages[paginate.value.pages.length - 1] >= 3 : paginate.value.pages[paginate.value.pages.length - 1] < totalPages.value - 1
);
const firstButtonIfCondition = computed(() => (isRtl.value ? paginate.value.pages[0] < totalPages.value : paginate.value.pages[0] >= 2));
const lastButtonIfCondition = computed(() =>
    isRtl.value ? paginate.value.pages[paginate.value.pages.length - 1] >= 2 : paginate.value.pages[paginate.value.pages.length - 1] < totalPages.value
);
const firstPageButtonIfCondition = computed(() => currentPageRef.value !== 1);
const lastPageButtonIfCondition = computed(() => currentPageRef.value !== totalPages.value);

if (props.type === 'link' && (props.linkUrl === '#' || !props.linkUrl.includes('[page]'))) {
    throw new TypeError('linkUrl must contain "[page]" if type is "link".');
}
</script>

<template>
    <ul id="componentContainer" :class="rootClasses">
        <!-- Go back to first page Button -->
        <li v-if="showEndingButtons && firstPageButtonIfCondition">
            <component
                :is="type === 'button' ? 'button' : 'a'"
                :href="navigationHandler(isRtl ? totalPages : 1)"
                :class="[firstPageButtonClass, paginatorClasses, disablePagination ? disabledPaginateButtonsClass : '']"
                :disabled="disablePagination"
                @click="emitPaginatedItems()"
                @click.prevent="onClickHandler(isRtl ? totalPages : 1)"
            >
                <slot name="first-page-button">
                    {{ firstPageContent }}
                </slot>
            </component>
        </li>

        <!-- Backward Jump Button -->
        <li v-if="showJumpButtons && startingBreakPointButtonIfCondition">
            <component
                :is="type === 'button' ? 'button' : 'a'"
                :href="navigationHandler(isRtl ? currentPageRef + Math.ceil(maxPagesShown / 2) : currentPageRef - Math.ceil(maxPagesShown / 2))"
                :class="[
                    backwardJumpButtonClass,
                    paginateButtonsClass,
                    disablePagination ? disabledPaginateButtonsClass : '',
                    disablePagination ? disabledBackwardJumpButtonClass : ''
                ]"
                :disabled="disablePagination"
                @click.prevent="onClickHandler(isRtl ? currentPageRef + Math.ceil(maxPagesShown / 2) : currentPageRef - Math.ceil(maxPagesShown / 2))"
                @click="emitPaginatedItems()"
            >
                <slot name="backward-jump-button">
                    {{ backwardJumpButtonContent }}
                </slot>
            </component>
        </li>

        <!-- Back Button -->
        <li v-if="!hidePrevNext && backButtonIfCondition">
            <component
                :is="type === 'button' ? 'button' : 'a'"
                :href="navigationHandler(isRtl ? currentPageRef + 1 : currentPageRef - 1)"
                :class="[
                    backButtonClass,
                    paginateButtonsClass,
                    disablePagination ? disabledPaginateButtonsClass : '',
                    disablePagination ? disabledBackButtonClass : ''
                ]"
                :disabled="disablePagination"
                @click.prevent="onClickHandler(isRtl ? currentPageRef + 1 : currentPageRef - 1)"
                @click="emitPaginatedItems()"
            >
                <slot name="prev-button">
                    {{ prevButtonContent }}
                </slot>
            </component>
        </li>

        <!-- First Button before Starting Breakpoint Button -->
        <li v-if="showBreakpointButtons && firstButtonIfCondition">
            <component
                :is="type === 'button' ? 'button' : 'a'"
                :href="navigationHandler(isRtl ? totalPages : 1)"
                :class="[
                    firstButtonClass,
                    paginateButtonsClass,
                    disablePagination ? disabledPaginateButtonsClass : '',
                    disablePagination ? disabledFirstButtonClass : ''
                ]"
                :disabled="disablePagination"
                @click.prevent="onClickHandler(isRtl ? totalPages : 1)"
                @click="emitPaginatedItems()"
            >
                {{ isRtl ? NumbersLocale(totalPages) : NumbersLocale(1) }}
            </component>
        </li>

        <!-- Starting Breakpoint Button -->
        <li v-if="showBreakpointButtons && startingBreakPointButtonIfCondition">
            <component
                :is="type === 'button' ? 'button' : 'a'"
                :href="
                    navigationHandler(
                        disableBreakpointButtons
                            ? currentPageRef
                            : isRtl
                              ? currentPageRef + Math.ceil(maxPagesShown / 2)
                              : currentPageRef - Math.ceil(maxPagesShown / 2)
                    )
                "
                :disabled="disableBreakpointButtons || disablePagination"
                :class="[
                    startingBreakpointButtonClass,
                    paginateButtonsClass,
                    disableBreakpointButtons || disablePagination ? `${disabledPaginateButtonsClass} ${disabledBreakPointButtonClass}` : ''
                ]"
                @click="emitPaginatedItems()"
                @click.prevent="
                    onClickHandler(
                        disableBreakpointButtons
                            ? currentPageRef
                            : isRtl
                              ? currentPageRef + Math.ceil(maxPagesShown / 2)
                              : currentPageRef - Math.ceil(maxPagesShown / 2)
                    )
                "
            >
                <slot name="starting-breakpoint-button">
                    {{ startingBreakpointContent }}
                </slot>
            </component>
        </li>

        <!-- Numbers Buttons -->
        <li v-for="(page, index) in paginate.pages" :key="index">
            <component
                :is="type === 'button' ? 'button' : 'a'"
                :href="navigationHandler(page)"
                :class="[
                    paginateButtonsClass,
                    numberButtonsClass,
                    page === currentPageRef ? activePaginator : '',
                    disablePagination ? disabledPaginateButtonsClass : ''
                ]"
                :disabled="disablePagination"
                @click.prevent="() => onClickHandler(page)"
                @click="emitPaginatedItems()"
            >
                {{ NumbersLocale(page) }}
            </component>
        </li>

        <!-- Ending Breakpoint Button -->
        <li v-if="showBreakpointButtons && endingBreakPointButtonIfCondition">
            <component
                :is="type === 'button' ? 'button' : 'a'"
                :href="
                    navigationHandler(
                        disableBreakpointButtons
                            ? currentPageRef
                            : isRtl
                              ? currentPageRef - Math.ceil(maxPagesShown / 2)
                              : currentPageRef + Math.ceil(maxPagesShown / 2)
                    )
                "
                :disabled="disableBreakpointButtons || disablePagination"
                :class="[
                    endingBreakPointButtonClass,
                    paginateButtonsClass,
                    disableBreakpointButtons || disablePagination ? `${disabledPaginateButtonsClass} ${disabledBreakPointButtonClass}` : ''
                ]"
                @click="emitPaginatedItems()"
                @click.prevent="
                    onClickHandler(
                        disableBreakpointButtons
                            ? currentPageRef
                            : isRtl
                              ? currentPageRef - Math.ceil(maxPagesShown / 2)
                              : currentPageRef + Math.ceil(maxPagesShown / 2)
                    )
                "
            >
                <slot name="ending-breakpoint-button">
                    {{ endingBreakpointButtonContent }}
                </slot>
            </component>
        </li>

        <!-- Last Button after Ending Breakingpoint Button-->
        <li v-if="showBreakpointButtons && lastButtonIfCondition">
            <component
                :is="type === 'button' ? 'button' : 'a'"
                :href="navigationHandler(isRtl ? 1 : totalPages)"
                :class="[
                    lastButtonClass,
                    paginateButtonsClass,
                    disablePagination ? disabledPaginateButtonsClass : '',
                    disablePagination ? disabledLastButtonClass : ''
                ]"
                :disabled="disablePagination"
                @click="emitPaginatedItems()"
                @click.prevent="onClickHandler(isRtl ? 1 : totalPages)"
            >
                {{ isRtl ? NumbersLocale(1) : NumbersLocale(totalPages) }}
            </component>
        </li>

        <!-- Next Button -->
        <li v-if="!hidePrevNext && nextButtonIfCondition">
            <component
                :is="type === 'button' ? 'button' : 'a'"
                :href="navigationHandler(isRtl ? currentPageRef - 1 : currentPageRef + 1)"
                :class="[
                    paginateButtonsClass,
                    nextButtonClass,
                    disablePagination ? disabledPaginateButtonsClass : '',
                    disablePagination ? disabledNextButtonClass : ''
                ]"
                :disabled="disablePagination"
                @click.prevent="onClickHandler(isRtl ? currentPageRef - 1 : currentPageRef + 1)"
                @click="emitPaginatedItems()"
            >
                <slot name="next-button">
                    {{ nextButtonContent }}
                </slot>
            </component>
        </li>

        <!-- Forward Jump Button -->
        <li v-if="showJumpButtons && endingBreakPointButtonIfCondition">
            <component
                :is="type === 'button' ? 'button' : 'a'"
                :href="navigationHandler(isRtl ? currentPageRef - Math.ceil(maxPagesShown / 2) : currentPageRef + Math.ceil(maxPagesShown / 2))"
                :class="[
                    forwardJumpButtonClass,
                    paginateButtonsClass,
                    disablePagination ? disabledPaginateButtonsClass : '',
                    disablePagination ? disabledForwardJumpButtonClass : ''
                ]"
                :disabled="disablePagination"
                @click.prevent="onClickHandler(isRtl ? currentPageRef - Math.ceil(maxPagesShown / 2) : currentPageRef + Math.ceil(maxPagesShown / 2))"
                @click="emitPaginatedItems()"
            >
                <slot name="forward-jump-button">
                    {{ forwardJumpButtonContent }}
                </slot>
            </component>
        </li>

        <!-- Go forward to last page -->
        <li v-if="showEndingButtons && lastPageButtonIfCondition">
            <component
                :is="type === 'button' ? 'button' : 'a'"
                :href="navigationHandler(isRtl ? 1 : totalPages)"
                :class="[lastPageButtonClass, paginateButtonsClass, disablePagination ? disabledPaginateButtonsClass : '']"
                :disabled="disablePagination"
                @click.prevent="onClickHandler(isRtl ? 1 : totalPages)"
                @click="emitPaginatedItems()"
            >
                <slot name="last-page-button">
                    {{ lastPageContent }}
                </slot>
            </component>
        </li>
    </ul>
</template>
