<script setup>
import { ref, toRefs, computed, watch, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
const emit = defineEmits(['update:modelValue', 'update:options']);
import { useArrayData } from 'src/composables/useArrayData';

const $props = defineProps({
    modelValue: {
        type: [String, Number, Object],
        default: null,
    },
    options: {
        type: Array,
        default: () => [],
    },
    optionLabel: {
        type: [String],
        default: 'name',
    },
    optionValue: {
        type: String,
        default: 'id',
    },
    dataKey: {
        type: String,
        default: null,
    },
    optionFilter: {
        type: String,
        default: null,
    },
    url: {
        type: String,
        default: '',
    },
    filterOptions: {
        type: [Array],
        default: () => [],
    },
    isClearable: {
        type: Boolean,
        default: true,
    },
    defaultFilter: {
        type: Boolean,
        default: true,
    },
    fields: {
        type: Array,
        default: null,
    },
    where: {
        type: Object,
        default: () => {},
    },
    sortBy: {
        type: String,
        default: null,
    },
    limit: {
        type: [Number, String],
        default: '30',
    },
    focusOnMount: {
        type: Boolean,
        default: false,
    },
});

const { t } = useI18n();
const requiredFieldRule = (val) => val ?? t('globals.fieldRequired');

const { optionLabel, optionValue, optionFilter, options, modelValue } = toRefs($props);
const myOptions = ref([]);
const myOptionsOriginal = ref([]);
const vnSelectRef = ref();

const selectValue = computed({
    get() {
        return $props.modelValue;
    },
    set(value) {
        arrayData.store.page = 0;
        emit('update:modelValue', value);
    },
});

function setOptions(data, append = true) {
    myOptions.value = JSON.parse(JSON.stringify(data));
    if (append) myOptionsOriginal.value = JSON.parse(JSON.stringify(data));
}
const useURL = computed(() => $props.url);
import { useAttrs } from 'vue';

const $attrs = useAttrs();
const arrayDataKey = $props.dataKey ?? ($props.url !== '' ? $props.url : $attrs.label);
const arrayDataOptions = {
    url: $props.url,
    where: $props.where,
    limit: $props.limit,
    sortBy: $props.sortBy,
    fields: $props.fields,
};
const arrayData = useArrayData(arrayDataKey, arrayDataOptions);
onMounted(async () => {
    setOptions(options.value);
    if (!$props.options) fetchFilter($props.modelValue);
    if (useURL.value) {
        arrayData.store.userFilter = $props.where;
        arrayData.store.filter.where = $props.where;
        const { data } = await arrayData.fetch({ append: true, updateRouter: false });
        setOptions(data);
    }
});

watch(modelValue, (newValue) => {
    if (useURL.value) return;
    if (!myOptions.value.some((option) => option[optionValue.value] == newValue))
        fetchFilter(newValue);
});
const isLoading = ref(false);

function filter(val, options) {
    const search = val.toString().toLowerCase();

    if (!search) return options;

    return options.filter((row) => {
        if ($props.filterOptions.length) {
            return $props.filterOptions.some((prop) => {
                const propValue = String(row[prop]).toLowerCase();
                return propValue.includes(search);
            });
        }

        const id = row.id;
        const optionLabel = String(row[$props.optionLabel]).toLowerCase();

        return id == search || optionLabel.includes(search);
    });
}

async function fetchFilter(val) {
    if (!useURL.value) return;

    const { fields, sortBy, limit } = $props;
    let key = new RegExp(/\d/g).test(val)
        ? optionFilter.value ?? optionValue.value
        : optionValue.value;

    const where = { ...{ [key]: { like: `%${val}%` } }, ...$props.where };
    const fetchOptions = { where, order: sortBy, limit };
    if (fields) fetchOptions.fields = fields;
    return arrayData.value.fetch(fetchOptions);
}

async function filterHandler(val, update) {
    if (!$props.defaultFilter) return update();
    let newOptions = [];
    if (myOptions.value.length > 0 && !useURL.value) {
        newOptions = filter(val, myOptions.value);
        myOptions.value = [];
    } else newOptions = filter(val, myOptionsOriginal.value);
    if (useURL.value && myOptions.value.length < 1) {
        arrayData.store.skip = 0;
        arrayData.store.filter.skip = 0;
        arrayData.store.filter.where = { [optionFilter.value]: val, ...$props.where };
        const { data } = await arrayData.fetch({ append: false });
        newOptions = data;
        myOptions.value = data;
    }
    update(
        () => {
            myOptions.value = newOptions;
        },
        (ref) => {
            if (val !== '' && ref.options.length > 0) {
                ref.setOptionIndex(-1);
                ref.moveOptionSelection(1, true);
            }
        }
    );
}

async function onScroll(scrollEv) {
    const { to, direction, from, index } = scrollEv;
    const lastIndex = myOptions.value.length - 1;

    if (from === 0 && index === 0) return;
    if (!useURL.value && !$props.fetchRef) return;
    if (direction === 'decrease') return;
    if (to === lastIndex && arrayData.store.hasMoreData && !isLoading.value) {
        isLoading.value = true;
        await arrayData.loadMore();
        setOptions(arrayData.store.data);
        vnSelectRef.value.scrollTo(lastIndex);
        isLoading.value = false;
    }
}

onMounted(async () => {
    if ($props.focusOnMount) setTimeout(() => vnSelectRef.value.showPopup(), 300);
});
</script>

<template>
    <QSelect
        :loading="isLoading"
        @virtual-scroll="onScroll"
        v-model="selectValue"
        :options="myOptions"
        :option-label="optionLabel"
        :option-value="optionValue"
        v-bind="$attrs"
        emit-value
        map-options
        use-input
        @filter="filterHandler"
        hide-selected
        fill-input
        ref="vnSelectRef"
        lazy-rules
        :class="{ required: $attrs.required }"
        :rules="$attrs.required ? [requiredFieldRule] : null"
        virtual-scroll-slice-size="options.length"
    >
        <template v-if="isClearable" #append>
            <QIcon
                v-show="value"
                name="close"
                @click.stop="value = null"
                class="cursor-pointer"
                size="xs"
            />
        </template>
        <template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName">
            <slot :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
        </template>
    </QSelect>
</template>

<style scoped lang="scss">
.q-field--outlined {
    max-width: 100%;
}
</style>
