<script setup>
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useArrayData } from 'composables/useArrayData';

const { t } = useI18n();

const props = defineProps({
    dataKey: {
        type: String,
        required: true,
    },
    class: {
        type: String,
        default: '',
    },
    autoLoad: {
        type: Boolean,
        default: false,
    },
    data: {
        type: Array,
        default: null,
    },
    url: {
        type: String,
        default: '',
    },
    filter: {
        type: Object,
        default: null,
    },
    userFilter: {
        type: Object,
        default: null,
    },
    where: {
        type: Object,
        default: null,
    },
    order: {
        type: [String, Array],
        default: '',
    },
    limit: {
        type: Number,
        default: 20,
    },
    userParams: {
        type: Object,
        default: null,
    },
    keepOpts: {
        type: Array,
        default: () => [],
    },
    offset: {
        type: Number,
        default: undefined,
    },
    skeleton: {
        type: Boolean,
        default: true,
    },
    exprBuilder: {
        type: Function,
        default: null,
    },
    searchUrl: {
        type: String,
        default: null,
    },
    disableInfiniteScroll: {
        type: Boolean,
        default: false,
    },
});

const emit = defineEmits(['onFetch', 'onPaginate', 'onChange']);
const isLoading = ref(false);
const mounted = ref(false);
const pagination = ref({
    sortBy: props.order,
    rowsPerPage: props.limit,
    page: 1,
});

const arrayData = useArrayData(props.dataKey, {
    url: props.url,
    filter: props.filter,
    userFilter: props.userFilter,
    where: props.where,
    limit: props.limit,
    order: props.order,
    userParams: props.userParams,
    exprBuilder: props.exprBuilder,
    keepOpts: props.keepOpts,
    searchUrl: props.searchUrl,
});
const store = arrayData.store;

onMounted(async () => {
    if (props.autoLoad && !store.data?.length) await fetch();
    mounted.value = true;
});

onBeforeUnmount(() => {
    arrayData.resetPagination();
    arrayData.reset(['currentFilter', 'userParams', 'userFilter']);
});

watch(
    () => props.data,
    () => {
        store.data = props.data;
    }
);

watch(
    () => store.data,
    (data) => {
        if (!mounted.value) return;
        emit('onChange', data);
    },
    { immediate: true }
);

watch(
    () => [props.url, props.filter],
    ([url, filter]) => mounted.value && fetch({ url, filter })
);
const addFilter = async (filter, params) => {
    await arrayData.addFilter({ filter, params });
};

async function fetch(params) {
    useArrayData(props.dataKey, params);
    arrayData.resetPagination();
    await arrayData.fetch({ append: false, updateRouter: mounted.value });
    return emitStoreData();
}

async function update(params) {
    useArrayData(props.dataKey, params);
    const { limit, skip } = store;
    store.limit = limit + skip;
    store.skip = 0;
    await arrayData.fetch({ append: false });
    store.limit = limit;
    store.skip = skip;
    return emitStoreData();
}

function emitStoreData() {
    if (!store.hasMoreData) isLoading.value = false;
    emit('onFetch', store.data);
    return store.data;
}

async function paginate() {
    const { page, rowsPerPage, sortBy, descending } = pagination.value;

    if (!props.url) return;

    isLoading.value = true;
    await arrayData.loadMore();
    if (!store.hasMoreData) {
        if (store.userParamsChanged) store.hasMoreData = true;
        store.userParamsChanged = false;
        endPagination();
        return;
    }

    pagination.value.rowsNumber = store.data.length;
    pagination.value.page = page;
    pagination.value.rowsPerPage = rowsPerPage;
    pagination.value.sortBy = sortBy;
    pagination.value.descending = descending;

    endPagination();
}

function endPagination() {
    isLoading.value = false;
    emit('onFetch', store.data);
    emit('onPaginate');
}
async function onLoad(index, done) {
    if (!store.data || !mounted.value) return done();

    if (store.data.length === 0 || !props.url) return done(false);

    pagination.value.page = pagination.value.page + 1;

    await paginate();
    let isDone = false;
    if (store.userParamsChanged) isDone = !store.hasMoreData;
    done(isDone);
}

defineExpose({
    fetch,
    update,
    addFilter,
    paginate,
    userParams: arrayData.store.userParams,
    currentFilter: arrayData.store.currentFilter,
});
</script>

<template>
    <div class="full-width">
        <div
            v-if="!props.autoLoad && !store.data && !isLoading"
            class="info-row q-pa-md text-center"
        >
            <h5>
                {{ t('No data to display') }}
            </h5>
        </div>
        <div
            v-if="props.skeleton && props.autoLoad && !store.data"
            class="card-list q-gutter-y-md"
        >
            <QCard class="card" v-for="$index in props.limit" :key="$index">
                <QItem v-ripple class="q-pa-none items-start cursor-pointer q-hoverable">
                    <QItemSection class="q-pa-md">
                        <QSkeleton type="rect" class="q-mb-md" square />
                        <QSkeleton type="text" square />
                        <QSkeleton type="text" class="q-mb-md" square />
                        <QSkeleton type="text" square />
                        <QSkeleton type="text" square />
                    </QItemSection>
                    <QSeparator vertical />
                    <QCardActions vertical class="justify-between">
                        <QSkeleton type="circle" class="q-mb-md" size="40px" />
                        <QSkeleton type="circle" class="q-mb-md" size="40px" />
                        <QSkeleton type="circle" class="q-mb-md" size="40px" />
                    </QCardActions>
                </QItem>
            </QCard>
        </div>
    </div>
    <QInfiniteScroll
        v-if="store.data"
        @load="onLoad"
        :offset="offset"
        :class="['full-width', props.class]"
        :disable="disableInfiniteScroll || !store.hasMoreData"
        v-bind="$attrs"
    >
        <slot name="body" :rows="store.data"></slot>
        <div v-if="isLoading" class="spinner info-row q-pa-md text-center">
            <QSpinner color="primary" size="md" />
        </div>
    </QInfiniteScroll>
</template>

<style lang="scss" scoped>
.spinner {
    z-index: 1;
    align-content: end;
    position: absolute;
    bottom: 0;
    left: 0;
}
.info-row {
    width: 100%;

    h5 {
        margin: 0;
    }
}
</style>

<i18n>
es:
    No data to display: Sin datos que mostrar
    No results found: No se han encontrado resultados
    Load more data: Cargar más resultados
</i18n>
