import { hasNormalizedSlot, normalizeSlot, installStoreManager, applyStoreManagerOptions } from '@vuecs/core';
import { inject, ref, provide, hasInjectionContext, isRef, defineComponent, toRef, h, resolveComponent, computed } from 'vue';
import { VCLink } from '@vuecs/link';

let instance;
function useNavigationProvider(module) {
    if (typeof instance !== 'undefined') {
        return instance;
    }
    if (typeof module === 'undefined') {
        throw new Error('A Navigation Provider must be set!');
    }
    instance = module;
    return instance;
}
function setNavigationProvider(module) {
    instance = module;
}
function createNavigationProvider(input) {
    return input;
}

const StoreSymbol = Symbol.for('VLNavigationStore');
function isStoreInjected() {
    if (!hasInjectionContext()) {
        return false;
    }
    const instance = inject(StoreSymbol);
    return !!instance;
}
function setupStore(app) {
    const store = {
        items: ref([]),
        itemsActive: ref([])
    };
    if (typeof app === 'undefined') {
        if (isStoreInjected()) {
            return;
        }
        provide(StoreSymbol, store);
        return;
    }
    if (app._context && app._context.provides && app._context.provides[StoreSymbol]) {
        return;
    }
    app.provide(StoreSymbol, store);
}
function injectStore() {
    const instance = inject(StoreSymbol);
    if (!instance) {
        throw new Error('The Store is not set.');
    }
    return instance;
}

function isNavigationItemMatch(one, two) {
    if (!one || !two) {
        return false;
    }
    if (one.id && two.id && one.id === two.id) {
        return true;
    }
    if (one.name && two.name && one.name === two.name) {
        return true;
    }
    if (one.url && two.url && one.url === two.url) {
        return true;
    }
    if (one.children && two.children && one.children.length === two.children.length) {
        for(let i = 0; i < one.children.length; i++){
            if (!isNavigationItemMatch(one.children[i], two.children[i])) {
                return false;
            }
        }
        return true;
    }
    return false;
}

// --------------------------------------------------
function setNavigationExpansion(items, item, parentMatch = false) {
    let matchInIteration = false;
    for(let i = 0; i < items.length; i++){
        const isMatch = isNavigationItemMatch(items[i], item);
        let isChildMatch = false;
        const { children } = items[i];
        if (typeof children !== 'undefined') {
            const { items: childItems, match: childMatch } = setNavigationExpansion(children, item, isMatch);
            items[i].children = childItems;
            isChildMatch = childMatch;
        }
        if (isMatch) {
            items[i].active = true;
        }
        if (isMatch || isChildMatch) {
            items[i].display = true;
            items[i].displayChildren = true;
        }
        if (parentMatch) {
            items[i].display = true;
        }
        if (!matchInIteration) {
            matchInIteration = isMatch || isChildMatch;
        }
    }
    if (matchInIteration) {
        for(let i = 0; i < items.length; i++){
            items[i].display = true;
        }
    }
    return {
        items,
        match: matchInIteration
    };
}

function resetNavigationItem(items, root = true) {
    for(let i = 0; i < items.length; i++){
        items[i].display = root;
        items[i].displayChildren = false;
        items[i].active = false;
        const { children } = items[i];
        if (typeof children !== 'undefined') {
            items[i].children = resetNavigationItem(children, false);
        }
    }
    return items;
}

function findNavigationItemsForTier(items, tier) {
    const filterFn = (component)=>typeof component.tier !== 'undefined' && component.tier === tier;
    return items.filter(filterFn);
}
function findNavigationItemForTier(items, tier) {
    const data = findNavigationItemsForTier(items, tier);
    if (data.length >= 1) {
        return data[0];
    }
    return undefined;
}
function setTierForNavigationItems(items, tier) {
    const mapFn = (component)=>{
        component.tier = tier;
        return component;
    };
    if (isRef(items)) {
        return items.value.map(mapFn);
    }
    return items.map(mapFn);
}
function removeTierFromNavigationItems(items, tier) {
    const filterFn = (items)=>typeof items.tier === 'undefined' || items.tier !== tier;
    if (isRef(items)) {
        return items.value.filter(filterFn);
    }
    return items.filter(filterFn);
}

function replaceNavigationTierItemActive(store, tier, item) {
    const items = removeTierFromNavigationItems(store.itemsActive.value, tier);
    if (item) {
        item.tier = tier;
        store.itemsActive.value = [
            ...items,
            item
        ];
    } else {
        store.itemsActive.value = items;
    }
}
function replaceNavigationTierItems(store, tier, items) {
    const componentsExisting = removeTierFromNavigationItems(store.items.value, tier);
    store.items.value = [
        ...componentsExisting,
        ...setTierForNavigationItems(items, tier)
    ];
}

function refreshNavigationTierItems(store, tier) {
    const components = resetNavigationItem(findNavigationItemsForTier(store.items.value, tier));
    const component = findNavigationItemForTier(store.itemsActive.value, tier);
    if (component) {
        const { items } = setNavigationExpansion(components, component);
        replaceNavigationTierItems(store, tier, items);
        return;
    }
    replaceNavigationTierItems(store, tier, components);
}

async function buildNavigationForTier(store, tier, itemsActive) {
    if (typeof itemsActive === 'undefined' || itemsActive.length === 0) {
        let tierStartIndex = 0;
        const tierEndIndex = tier;
        itemsActive = [];
        while(tierStartIndex <= tierEndIndex){
            const component = findNavigationItemForTier(store.itemsActive.value, tierStartIndex);
            if (!component) {
                break;
            }
            itemsActive.push(component);
            tierStartIndex++;
        }
    }
    const items = await useNavigationProvider().getItems(tier, itemsActive);
    if (typeof items === 'undefined') {
        return false;
    }
    replaceNavigationTierItems(store, tier, items);
    refreshNavigationTierItems(store, tier);
    return true;
}

function flattenNestedNavigationItems(items) {
    const output = [];
    for(let i = 0; i < items.length; i++){
        const { children, ...data } = items[i];
        output.push(data);
        if (children && children.length > 0) {
            output.push(...flattenNestedNavigationItems([
                ...children
            ]));
        }
    }
    return output;
}

async function selectNavigationTierItem(store, tier, component) {
    const isMatch = isNavigationItemMatch(findNavigationItemForTier(store.itemsActive.value, tier), component);
    if (isMatch) {
        return;
    }
    replaceNavigationTierItemActive(store, tier, component);
    refreshNavigationTierItems(store, tier);
    tier++;
    // eslint-disable-next-line no-constant-condition
    while(true){
        const built = await buildNavigationForTier(store, tier);
        if (!built) {
            break;
        }
        tier++;
    }
}

function toggleNavigation(store, tier, component) {
    const isMatch = component.displayChildren || isNavigationItemMatch(findNavigationItemForTier(store.itemsActive.value, tier), component);
    if (isMatch) {
        replaceNavigationTierItemActive(store, tier, undefined);
    } else {
        replaceNavigationTierItemActive(store, tier, component);
    }
    refreshNavigationTierItems(store, tier);
}

function isAbsoluteURL(str) {
    return str.substring(0, 7) === 'http://' || str.substring(0, 8) === 'https://';
}

var SlotName;
(function(SlotName) {
    SlotName["ITEM"] = "item";
    SlotName["SEPARATOR"] = "separator";
    SlotName["LINK"] = "link";
    SlotName["SUB"] = "sub";
    SlotName["SUB_TITLE"] = "sub-title";
    SlotName["SUB_ITEMS"] = "sub-items";
})(SlotName || (SlotName = {}));
var ElementType;
(function(ElementType) {
    ElementType["LINK"] = "link";
    ElementType["SEPARATOR"] = "separator";
})(ElementType || (ElementType = {}));

const VCNavItem = defineComponent({
    props: {
        tier: {
            type: Number,
            default: 0
        },
        component: {
            type: Object,
            required: true
        }
    },
    setup (props, { slots }) {
        const store = injectStore();
        const component = toRef(props, 'component');
        const selectComponent = async (value)=>{
            await selectNavigationTierItem(store, props.tier, value);
        };
        const toggleComponentExpansion = async (value)=>toggleNavigation(store, props.tier, value);
        return ()=>{
            const buildItem = ()=>{
                let item;
                switch(component.value.type){
                    case ElementType.SEPARATOR:
                        {
                            const hasSlot = hasNormalizedSlot(SlotName.SEPARATOR, slots);
                            if (hasSlot) {
                                item = normalizeSlot(SlotName.SEPARATOR, {
                                    component: component.value
                                }, slots);
                            } else {
                                item = h('div', {
                                    class: 'nav-separator'
                                }, component.value.name);
                            }
                            break;
                        }
                    default:
                        {
                            if (typeof component.value.children === 'undefined') {
                                const hasSlot = hasNormalizedSlot(SlotName.LINK, slots);
                                if (hasSlot) {
                                    item = normalizeSlot(SlotName.LINK, {
                                        component: component.value,
                                        selectComponent,
                                        isActive: component.value.active
                                    }, slots);
                                } else {
                                    const linkProps = {
                                        active: component.value.active,
                                        disabled: false,
                                        prefetch: true
                                    };
                                    if (component.value.url) {
                                        if (isAbsoluteURL(component.value.url) || component.value.url.startsWith('#')) {
                                            linkProps.href = component.value.url;
                                            if (component.value.urlTarget) {
                                                linkProps.target = component.value.urlTarget;
                                            }
                                        } else {
                                            linkProps.to = component.value.url;
                                        }
                                    }
                                    item = h(VCLink, {
                                        class: [
                                            'nav-link',
                                            {
                                                'root-link': component.value.root
                                            }
                                        ],
                                        ...linkProps,
                                        onClicked () {
                                            if (!component.value.url) {
                                                return selectComponent.call(null, component.value);
                                            }
                                            return undefined;
                                        },
                                        onClick () {
                                            return selectComponent.call(null, component.value);
                                        }
                                    }, {
                                        default: ()=>[
                                                ...component.value.icon ? [
                                                    h('i', {
                                                        class: component.value.icon
                                                    })
                                                ] : [],
                                                h('span', {
                                                    class: 'nav-link-text'
                                                }, [
                                                    component.value.name
                                                ])
                                            ]
                                    });
                                }
                            } else if (hasNormalizedSlot(SlotName.SUB, slots)) {
                                item = normalizeSlot(SlotName.SUB, {
                                    component: component.value,
                                    selectComponent,
                                    toggleComponentExpansion
                                }, slots);
                            } else {
                                let title;
                                if (hasNormalizedSlot(SlotName.SUB_TITLE, slots)) {
                                    title = normalizeSlot(SlotName.SUB_TITLE, {
                                        component: component.value,
                                        selectComponent,
                                        toggleComponentExpansion
                                    });
                                } else {
                                    title = h('div', {
                                        class: 'nav-sub-title',
                                        onClick ($event) {
                                            $event.preventDefault();
                                            return toggleComponentExpansion.call(null, component.value);
                                        }
                                    }, [
                                        [
                                            ...component.value.icon ? [
                                                h('i', {
                                                    class: component.value.icon
                                                })
                                            ] : [],
                                            h('span', {
                                                class: 'nav-link-text'
                                            }, [
                                                component.value.name
                                            ])
                                        ]
                                    ]);
                                }
                                let items;
                                if (hasNormalizedSlot(SlotName.SUB_ITEMS, slots)) {
                                    items = normalizeSlot(SlotName.SUB_ITEMS, {
                                        component: component.value,
                                        selectComponent,
                                        toggleComponentExpansion
                                    });
                                } else if (component.value.displayChildren) {
                                    const navigationComponents = resolveComponent('VCNavItems');
                                    items = h(navigationComponents, {
                                        class: 'list-unstyled nav-sub-items',
                                        tier: props.tier,
                                        entities: component.value.children
                                    });
                                }
                                item = [
                                    title,
                                    items
                                ];
                            }
                            break;
                        }
                }
                return item;
            };
            return h('div', {
                class: [
                    'nav-item',
                    {
                        active: component.value.active || component.value.displayChildren
                    }
                ]
            }, [
                buildItem()
            ]);
        };
    }
});

const VCNavItems = defineComponent({
    props: {
        tier: {
            type: Number,
            default: 0
        },
        entities: {
            type: Array,
            default: undefined
        }
    },
    setup (props, { slots }) {
        const store = injectStore();
        const items = computed(()=>{
            if (typeof props.entities !== 'undefined') {
                return props.entities;
            }
            return findNavigationItemsForTier(store.items.value, props.tier);
        });
        const buildChild = (context)=>{
            if (hasNormalizedSlot(SlotName.ITEM, slots)) {
                return normalizeSlot(SlotName.ITEM, context, slots);
            }
            return h(VCNavItem, context);
        };
        const buildChildren = ()=>{
            const entities = [];
            if (items.value) {
                for(let i = 0; i < items.value.length; i++){
                    if (items.value[i].display) {
                        entities.push(h('li', {
                            key: i
                        }, [
                            buildChild({
                                tier: props.tier,
                                component: items.value[i]
                            })
                        ]));
                    }
                }
            }
            return entities;
        };
        return ()=>h('ul', {
                class: 'nav-items'
            }, [
                buildChildren()
            ]);
    }
});

async function buildNavigation(context = {}) {
    const store = injectStore();
    const navigationProvider = useNavigationProvider();
    let itemsActive = [];
    if (typeof context.itemsActive !== 'undefined') {
        itemsActive = context.itemsActive;
    } else if (context.route) {
        if (typeof navigationProvider.getItemsActiveByRoute !== 'undefined') {
            itemsActive = await navigationProvider.getItemsActiveByRoute(context.route);
        } else if (typeof navigationProvider.getItemsActiveByURL !== 'undefined') {
            itemsActive = await navigationProvider.getItemsActiveByURL(context.route.fullPath);
        }
    } else if (context.url && typeof navigationProvider.getItemsActiveByURL !== 'undefined') {
        itemsActive = await navigationProvider.getItemsActiveByURL(context.url);
    }
    if (itemsActive.length > 0) {
        for(let i = 0; i < itemsActive.length; i++){
            if (typeof itemsActive[i].tier === 'undefined') {
                itemsActive[i].tier = i;
            }
        }
    }
    let tierIndex = 0;
    let url;
    if (typeof context.url === 'string') {
        url = context.url;
    } else if (typeof context.route !== 'undefined') {
        url = context.route.fullPath;
    }
    // eslint-disable-next-line no-constant-condition
    while(true){
        let items = await navigationProvider.getItems(tierIndex, itemsActive);
        if (!items || items.length === 0) {
            break;
        }
        // ensure tier property
        items = setTierForNavigationItems(items, tierIndex);
        let currentItem = findNavigationItemForTier(itemsActive, tierIndex);
        if (!currentItem) {
            if (url) {
                const urlMatches = items.filter((item)=>isNavigationItemMatch(item, {
                        url
                    }));
                if (urlMatches.length > 0) {
                    [currentItem] = urlMatches;
                }
            }
            if (!currentItem) {
                const defaultItem = items.filter((item)=>item.default);
                if (defaultItem.length > 0) {
                    currentItem = defaultItem;
                } else {
                    [currentItem] = items;
                }
            }
            currentItem.tier = tierIndex;
            itemsActive.push(currentItem);
        }
        if (!currentItem) {
            continue;
        }
        replaceNavigationTierItemActive(store, tierIndex, currentItem);
        await buildNavigationForTier(store, tierIndex, itemsActive);
        tierIndex++;
    }
}

function install(instance, options) {
    if (options.provider) {
        setNavigationProvider(options.provider);
    }
    setupStore(instance);
    const storeManager = installStoreManager(instance);
    if (options.storeManager) {
        applyStoreManagerOptions(storeManager, options.storeManager);
    }
    Object.entries({
        VCNavItem,
        VCNavItems
    }).forEach(([componentName, component])=>{
        instance.component(componentName, component);
    });
}
var index = {
    install
};

export { VCNavItem, VCNavItems, buildNavigation, buildNavigationForTier, createNavigationProvider, index as default, flattenNestedNavigationItems, install, setNavigationProvider, useNavigationProvider };
//# sourceMappingURL=index.mjs.map
