<script setup>
import { onMounted, ref, computed, watch, onUnmounted } from 'vue';
import { useRoute } from 'vue-router';
import VnInput from 'src/components/common/VnInput.vue';
import { useState } from 'src/composables/useState';
import axios from 'axios';
import { useArrayData } from 'composables/useArrayData';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';

const props = defineProps({
    rootLabel: {
        type: String,
        default: 'Locations',
    },
    tickedNodes: {
        type: Array,
        default: () => [],
    },
    showSearchBar: {
        type: Boolean,
        default: false,
    },
    showDefaultCheckboxes: {
        type: Boolean,
        default: false,
    },
});

const emit = defineEmits(['update:tickedNodes']);

const route = useRoute();
const state = useState();

const treeRef = ref();
const expanded = ref([]);

const datakey = 'ZoneLocations';
const url = computed(() => `Zones/${route.params.id}/getLeaves`);
const arrayData = useArrayData(datakey, {
    url: url.value,
    limit: null,
});
const store = arrayData.store;

const defaultNode = {
    id: null,
    name: props.rootLabel,
    sons: true,
    tickable: false,
    noTick: true,
    children: [{}],
};
const nodes = ref([defaultNode]);

const _tickedNodes = computed({
    get: () => props.tickedNodes,
    set: (value) => emit('update:tickedNodes', value),
});

const previousExpandedNodes = ref(new Set());

const onNodeExpanded = async (nodeKeysArray) => {
    let nodeKeysSet = new Set(nodeKeysArray);
    const lastNodeKey = nodeKeysArray.at(-1);

    if (!nodeKeysSet.has(null)) return;

    const wasExpanded = !previousExpandedNodes.value.has(lastNodeKey);
    if (wasExpanded && treeRef.value) {
        const node = treeRef.value?.getNodeByKey(lastNodeKey);
        const params = { parentId: node.id };
        const response = await axios.get(`Zones/${route.params.id}/getLeaves`, {
            params,
        });
        if (response.data) {
            node.childs = response.data.map((n) => {
                if (n.sons > 0) n.childs = [{}];
                n.selected = isSelected(n.selected);
                return n;
            });
        }
        await fetchNodeLeaves(lastNodeKey, true);
    } else {
        const difference = new Set(
            [...previousExpandedNodes.value].filter((x) => !nodeKeysSet.has(x))
        );
        const collapsedNode = Array.from(difference).pop();
        const node = treeRef.value?.getNodeByKey(collapsedNode);
        const allNodeIds = getNodeIds(node);
        expanded.value = expanded.value.filter((id) => !allNodeIds.includes(id));
    }
    previousExpandedNodes.value = nodeKeysSet;
};

const fetchNodeLeaves = async (nodeKey) => {
    if (!treeRef.value) return;
    const node = treeRef.value?.getNodeByKey(nodeKey);
    if (typeof node.selected === 'number') node.selected = !!node.selected;
    if (node.sons > 0 && !node.childs) {
        node.childs = [{}];
        const index = expanded.value.indexOf(node.id);
        expanded.value.splice(index, 1);
    }
    if (!node || node.sons === 0) return;
};

function getNodeIds(node) {
    if (!node) return [];
    let ids = [];
    if (node.id) ids.push(node.id);

    if (node.childs)
        node.childs.forEach((child) => {
            ids = ids.concat(getNodeIds(child));
        });
    return ids;
}

function isSelected(selected) {
    if (typeof selected === 'number') return !!selected;
}

watch(
    () => store.data,
    async (val) => {
        if (!val) return;
        // // Se triggerea cuando se actualiza el store.data, el cual es el resultado del fetch de la searchbar
        if (!nodes.value[0]) nodes.value = [defaultNode];
        nodes.value[0].childs = [...val];
        const fetchedNodeKeys = val.flatMap(getNodeIds);
        state.set('Tree', [...fetchedNodeKeys]);
        expanded.value = [null, ...fetchedNodeKeys];
        for (let n of state.get('Tree')) {
            await fetchNodeLeaves(n);
        }
        previousExpandedNodes.value = new Set(expanded.value);
    },
    { immediate: true }
);

const reFetch = async () => {
    await arrayData.fetch({});
};

onMounted(async () => {
    await reFetch();
});

onUnmounted(() => {
    state.set('Tree', undefined);
    arrayData.destroy();
});
</script>

<template>
    <VnInput
        v-if="showSearchBar"
        v-model="store.userParams.search"
        :placeholder="$t('globals.search')"
        @keydown.enter.stop.prevent="reFetch"
    >
        <template #prepend>
            <QBtn color="primary" icon="search" dense flat @click="reFetch()" />
        </template>
    </VnInput>
    <VnSearchbar v-if="!showSearchBar" :data-key="datakey" :url="url" :redirect="false" />
    <QTree
        ref="treeRef"
        :nodes="nodes"
        node-key="id"
        label-key="name"
        children-key="childs"
        :tick-strategy="props.showDefaultCheckboxes ? 'strict' : 'none'"
        v-model:expanded="expanded"
        @update:expanded="onNodeExpanded($event)"
        v-model:ticked="_tickedNodes"
        :default-expand-all="true"
    >
        <template #default-header="{ node }">
            <div
                :id="node.id"
                class="qtr row justify-between full-width q-pr-md cursor-pointer"
            >
                <slot name="content" :node="node" />
            </div>
        </template>
    </QTree>
</template>
