<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';

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 arrayData = useArrayData('ZoneLocations', {
    url: `Zones/${route.params.id}/getLeaves`,
});
const { store } = arrayData;
const storeData = computed(() => store.data);

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

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) await fetchNodeLeaves(lastNodeKey);
    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 formatNodeSelected = (node) => {
    if (node.selected === 1) node.selected = true;
    else if (node.selected === 0) node.selected = false;

    if (node.childs && node.childs.length > 0) {
        expanded.value.push(node.id);

        node.childs.forEach((childNode) => {
            formatNodeSelected(childNode);
        });
    }

    if (node.sons > 0 && !node.childs) node.childs = [{}];
};

const fetchNodeLeaves = async (nodeKey) => {
    try {
        const node = treeRef.value?.getNodeByKey(nodeKey);
        if (!node || node.sons === 0) return;

        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) => {
                formatNodeSelected(n);
                return n;
            });
        }

        state.set('Tree', node);
    } catch (err) {
        console.error('Error fetching department leaves', err);
        throw new Error();
    }
};

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

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

watch(storeData, async (val) => {
    // Se triggerea cuando se actualiza el store.data, el cual es el resultado del fetch de la searchbar
    nodes.value[0].childs = [...val];
    const fetchedNodeKeys = val.flatMap(getNodeIds);
    state.set('Tree', [...fetchedNodeKeys]);

    if (store.userParams?.search === '') {
        val.forEach((n) => {
            formatNodeSelected(n);
        });
    } else {
        for (let n of state.get('Tree')) await fetchNodeLeaves(n);
        expanded.value = [null, ...fetchedNodeKeys];
    }
    previousExpandedNodes.value = new Set(expanded.value);
});

const reFetch = async () => {
    const { data } = await arrayData.fetch({ append: false });
    nodes.value = data;
};

onMounted(async () => {
    if (store.userParams?.search && !props.showSearchBar) {
        await reFetch();
        return;
    }
    const stateTree = state.get('Tree');
    const tree = stateTree ? [...state.get('Tree')] : [null];
    const lastStateTree = state.get('TreeState');
    if (tree) {
        for (let n of tree) {
            await fetchNodeLeaves(n);
        }

        if (lastStateTree) {
            tree.push(lastStateTree);
            await fetchNodeLeaves(lastStateTree);
        }
    }

    setTimeout(() => {
        if (lastStateTree) {
            document.getElementById(lastStateTree).scrollIntoView();
        }
    }, 1000);

    expanded.value.unshift(null);
    previousExpandedNodes.value = new Set(expanded.value);
});

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

<template>
    <VnInput
        v-if="showSearchBar"
        v-model="store.userParams.search"
        :placeholder="$t('globals.search')"
        @update:model-value="reFetch()"
    >
        <template #prepend>
            <QBtn color="primary" icon="search" dense flat @click="reFetch()" />
        </template>
    </VnInput>
    <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>
