import { hasOwnProperty, hasNormalizedSlot, normalizeSlot, applyPluginBaseOptions } from '@vue-layout/core';
import { unref, isRef, ref, defineComponent, resolveDynamicComponent, computed, h, toRef, resolveComponent } from 'vue';

/*
 * Copyright (c) 2022-2022.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */ let instance$1;
function useNavigationProvider(module) {
    if (typeof instance$1 !== 'undefined') {
        return instance$1;
    }
    if (typeof module === 'undefined') {
        throw new Error('A Navigation Provider must be set!');
    }
    instance$1 = module;
    return instance$1;
}
function setNavigationProvider(module) {
    instance$1 = module;
}

function isRootLink(element) {
    element = unref(element);
    return element.rootLink || element.url && element.url === '/';
}
function isNavigationElementMatch(one, two, exact = true) {
    one = unref(one);
    two = unref(two);
    // both undefined or one of them
    if (!(one && two) || !one || !two) {
        return false;
    }
    // check when true
    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) {
        if (one.url === two.url) {
            return true;
        }
        if (!exact && !isRootLink(one) && !isRootLink(two)) {
            if (one.url.startsWith(two.url) || two.url.startsWith(one.url)) {
                return true;
            }
        }
    }
    if (one.children && two.children && one.children.length === two.children.length) {
        for(let i = 0; i < one.children.length; i++){
            if (!isNavigationElementMatch(one.children[i], two.children[i])) {
                return false;
            }
        }
        return true;
    }
    return false;
}

// --------------------------------------------------
function resetNavigationExpansion(items, rootTier = true) {
    for(let i = 0; i < items.length; i++){
        items[i].display = rootTier;
        items[i].displayChildren = false;
        const { children } = items[i];
        if (typeof children !== 'undefined') {
            items[i].children = resetNavigationExpansion(children, false);
        }
    }
    return items;
}
function setNavigationExpansion(items, item, parentMatch = false) {
    let matchInIteration = false;
    for(let i = 0; i < items.length; i++){
        const isMatch = isNavigationElementMatch(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 || 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
    };
}

/*
 * Copyright (c) 2022.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */ function flattenNestedNavigationElements(items) {
    const output = [];
    for(let i = 0; i < items.length; i++){
        const { children, ...data } = items[i];
        output.push(data);
        if (typeof children !== 'undefined') {
            output.push(...flattenNestedNavigationElements([
                ...children
            ]));
        }
    }
    return output;
}

/*
 * Copyright (c) 2022-2022.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */ function reduceNavigationElementsByRestriction(items, context) {
    const result = [];
    for(let i = 0; i < items.length; i++){
        if (typeof items[i].requireLoggedIn !== 'undefined' && items[i].requireLoggedIn && !context.isLoggedIn()) {
            continue;
        }
        if (typeof items[i].requireLoggedOut !== 'undefined' && items[i].requireLoggedOut && context.isLoggedIn()) {
            continue;
        }
        let canPass = true;
        if (typeof items[i].requirePermissions !== 'undefined' && canPass) {
            let checks = items[i].requirePermissions;
            if (typeof checks === 'function') {
                if (!checks(context.hasPermission)) {
                    canPass = false;
                }
            } else if (Array.isArray(checks)) {
                checks = checks.filter((item)=>item);
                if (checks.length > 0 && !checks.some((item)=>context.hasPermission(item))) {
                    canPass = false;
                }
            }
        }
        if (canPass) {
            const { children } = items[i];
            if (children) {
                items[i].children = reduceNavigationElementsByRestriction(children, context);
            }
            result.push(items[i]);
        }
    }
    return result;
}

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

/*
 * Copyright (c) 2022-2022.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */ function isAbsoluteURL(str) {
    return str.substring(0, 7) === 'http://' || str.substring(0, 8) === 'https://';
}

let instance;
function useStore() {
    if (typeof instance !== 'undefined') {
        return instance;
    }
    instance = ref({
        items: [],
        itemsActive: [],
        tiers: undefined
    });
    return instance;
}
function useStoreItem(key) {
    const store = useStore();
    return store.value[key];
}
function setStoreItem(key, value) {
    const store = useStore();
    store.value[key] = value;
}
function setStore(store, options) {
    options = options || {};
    if (typeof instance !== 'undefined' && !options.forceSet) {
        return instance;
    }
    instance = store;
    return instance;
}
function getStore() {
    return instance;
}

function setNavigationElementActive(tier, item) {
    const data = useStoreItem('itemsActive');
    const componentsExisting = removeTierFromNavigationElements(data, tier);
    if (item) {
        item.tier = tier;
        setStoreItem('itemsActive', [
            ...componentsExisting,
            item
        ]);
    } else {
        setStoreItem('itemsActive', componentsExisting);
    }
}
function setNavigationElements(tier, items) {
    const data = useStoreItem('items');
    const componentsExisting = removeTierFromNavigationElements(data, tier);
    setStoreItem('items', [
        ...componentsExisting,
        ...setTierForNavigationElements(items, tier)
    ]);
}
function refreshNavigationElements(tier) {
    let components = getComponents(tier);
    components = resetNavigationExpansion(components);
    const component = getActiveComponent(tier);
    if (component) {
        const { items } = setNavigationExpansion(components, component);
        components = items;
    }
    setNavigationElements(tier, components);
}
async function calculateNavigationTiers() {
    let tiers = useStoreItem('tiers');
    if (typeof tiers !== 'undefined') {
        return tiers;
    }
    tiers = 0;
    let match = true;
    while(match){
        const hasTier = await useNavigationProvider().hasTier(tiers);
        if (!hasTier) {
            match = false;
        } else {
            tiers++;
        }
    }
    setStoreItem('tiers', tiers);
    return tiers;
}
// --------------------------------------------------------
async function selectNavigationElement(tier, component) {
    const isMatch = isNavigationElementMatch(getActiveComponent(tier), component);
    if (isMatch) {
        return;
    }
    setNavigationElementActive(tier, component);
    refreshNavigationElements(tier);
    const tiers = await calculateNavigationTiers();
    let tierStartIndex = tier + 1;
    while(tierStartIndex <= tiers){
        await buildNavigationForTier(tierStartIndex);
        tierStartIndex++;
    }
}
function toggleNavigation(tier, component) {
    const isMatch = isNavigationElementMatch(getActiveComponent(tier), component) || component.displayChildren;
    if (isMatch) {
        setNavigationElementActive(tier, undefined);
    } else {
        setNavigationElementActive(tier, component);
    }
    refreshNavigationElements(tier);
}
async function buildNavigation(context) {
    const navigationProvider = useNavigationProvider();
    context = context || {};
    let itemsActive = context.items ?? [];
    if (itemsActive.length === 0 && context.url) {
        itemsActive = await navigationProvider.getElementsActive(context.url);
        for(let i = 0; i < itemsActive.length; i++){
            itemsActive[i].tier = i;
        }
    }
    const tierLength = await calculateNavigationTiers();
    let tierIndex = 0;
    let tierHasItems = true;
    while(tierIndex < tierLength && tierHasItems){
        let items = await navigationProvider.getElements(tierIndex, [
            ...itemsActive
        ]);
        if (items.length === 0) {
            tierHasItems = false;
            continue;
        }
        // ensure tier property
        items = setTierForNavigationElements(items, tierIndex);
        let currentItem = findNavigationElementForTier(itemsActive, tierIndex);
        if (!currentItem) {
            if (context.url) {
                const urlMatches = items.filter((item)=>isNavigationElementMatch(item, {
                        url: context?.url
                    }));
                if (urlMatches && urlMatches.length > 0) {
                    // eslint-disable-next-line prefer-destructuring
                    currentItem = urlMatches[0];
                }
            }
            if (!currentItem) {
                const defaultItem = items.filter((item)=>item.default);
                if (defaultItem.length > 0) {
                    currentItem = defaultItem;
                } else {
                    // eslint-disable-next-line prefer-destructuring
                    currentItem = items[0];
                }
            }
            currentItem.tier = tierIndex;
            itemsActive.push(currentItem);
        }
        if (!currentItem) {
            continue;
        }
        setNavigationElementActive(tierIndex, currentItem);
        await buildNavigationForTier(tierIndex, itemsActive);
        tierIndex++;
    }
}
async function buildNavigationForTier(tier, itemsActive) {
    if (typeof itemsActive === 'undefined') {
        let tierStartIndex = 0;
        const tierEndIndex = tier;
        itemsActive = [];
        let found = true;
        while(tierStartIndex <= tierEndIndex && found){
            const component = getActiveComponent(tierStartIndex);
            if (!component) {
                found = false;
                continue;
            }
            itemsActive.push(component);
            tierStartIndex++;
        }
    }
    const items = await useNavigationProvider().getElements(tier, itemsActive);
    setNavigationElements(tier, [
        ...items
    ]);
    refreshNavigationElements(tier);
}
// --------------------------------------------------------
function getComponents(tier) {
    const data = useStoreItem('items');
    return findNavigationElementsForTier(data, tier);
}
function getActiveComponent(tier) {
    const data = useStoreItem('itemsActive');
    return findNavigationElementForTier(data, tier);
} // --------------------------------------------------------

const MyLink = defineComponent({
    name: 'MyLink',
    props: {
        active: {
            type: Boolean,
            default: false
        },
        disabled: {
            type: Boolean,
            default: false
        },
        href: {
            type: String,
            default: undefined
        },
        prefetch: {
            type: Boolean,
            default: true
        },
        target: {
            type: String,
            default: '_self'
        },
        to: {
            type: String,
            default: undefined
        }
    },
    emits: [
        'click',
        'clicked'
    ],
    setup (props, { emit, slots }) {
        const routerLink = resolveDynamicComponent('RouterLink');
        const nuxtLink = resolveDynamicComponent('NuxtLink');
        const computedTag = computed(()=>{
            const hasRouter = typeof routerLink !== 'string';
            if (!hasRouter || props.disabled || !props.to) {
                return 'a';
            }
            if (typeof nuxtLink !== 'string') {
                return 'nuxt-link';
            }
            return 'router-link';
        });
        const isRouterLink = computed(()=>computedTag.value !== 'a');
        const computedHref = computed(()=>{
            if (props.href) {
                return props.href;
            }
            if (isRouterLink.value) {
                return null;
            }
            return '#';
        });
        const computedProps = computed(()=>{
            if (!isRouterLink.value) {
                return {};
            }
            return {
                ...props.to ? {
                    to: props.to
                } : {},
                ...typeof props.prefetch !== 'undefined' ? {
                    prefetch: props.prefetch
                } : {}
            };
        });
        const computedAttrs = computed(()=>({
                ...props.href ? {
                    href: props.href
                } : {},
                ...isRouterLink.value ? {} : {
                    target: props.target
                }
            }));
        const buildVNodeProps = ()=>{
            const onClick = (event)=>{
                const isEvent = hasOwnProperty(event, 'preventDefault') && hasOwnProperty(event, 'stopPropagation') && hasOwnProperty(event, 'stopImmediatePropagation');
                if (isEvent && props.disabled) {
                    event.preventDefault();
                    event.stopPropagation();
                    event.stopImmediatePropagation();
                } else {
                    if (isRouterLink.value) {
                        emit('click', event);
                    }
                    emit('clicked', event);
                }
                if (isEvent && !isRouterLink.value && computedHref.value === '#') {
                    event.preventDefault();
                    event.stopPropagation();
                    event.stopImmediatePropagation();
                }
            };
            const vNodeProps = {
                class: {
                    active: props.active,
                    disabled: props.disabled
                },
                ...computedAttrs.value,
                ...computedProps.value,
                onClick
            };
            return vNodeProps;
        };
        let component;
        switch(computedTag.value){
            case 'router-link':
                component = routerLink;
                break;
            case 'nuxt-link':
                component = nuxtLink;
                break;
            default:
                component = 'a';
        }
        if (typeof component === 'string') {
            return ()=>h(component, buildVNodeProps(), [
                    typeof slots.default === 'function' ? slots.default() : []
                ]);
        }
        return ()=>h(component, buildVNodeProps(), {
                default: ()=>typeof slots.default === 'function' ? slots.default() : []
            });
    }
});

/*
 * Copyright (c) 2022-2022.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */ 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 = {}));

const NavigationComponent = defineComponent({
    name: 'NavigationComponent',
    props: {
        tier: {
            type: Number,
            default: 0
        },
        component: {
            type: Object
        }
    },
    setup (props, { slots }) {
        const component = toRef(props, 'component');
        const componentActive = computed(()=>getActiveComponent(props.tier));
        const isStrictMatch = computed(()=>isNavigationElementMatch(componentActive, component));
        const isMatch = computed(()=>isNavigationElementMatch(componentActive, component, false));
        const selectComponent = async (value)=>{
            await selectNavigationElement(props.tier, value);
        };
        const toggleComponentExpansion = async (value)=>{
            await toggleNavigation(props.tier, value);
        };
        const buildItem = ()=>{
            let item;
            switch(component.value.type){
                case '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: isMatch
                                }, slots);
                            } else {
                                const linkProps = {
                                    active: unref(isMatch),
                                    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(MyLink, {
                                    class: [
                                        'nav-link',
                                        {
                                            'router-link-active': isMatch.value,
                                            'router-link-exact-active': isStrictMatch.value,
                                            active: isMatch.value,
                                            'root-link': component.value.rootLink
                                        }
                                    ],
                                    ...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('NavigationComponents');
                                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: isMatch.value || component.value.displayChildren
                    }
                ]
            }, [
                buildItem()
            ]);
    }
});

const NavigationComponents = defineComponent({
    name: 'NavigationComponents',
    props: {
        tier: {
            type: Number,
            default: 0
        },
        entities: {
            type: Array,
            default: undefined
        }
    },
    setup (props, { slots }) {
        useStore();
        const items = computed(()=>{
            if (typeof props.entities !== 'undefined') {
                return props.entities;
            }
            return getComponents(props.tier);
        });
        const buildChild = (context)=>{
            if (hasNormalizedSlot(SlotName.ITEM, slots)) {
                return normalizeSlot(SlotName.ITEM, context, slots);
            }
            return h(NavigationComponent, 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());
    }
});

function install(instance, options) {
    if (options.provider) {
        setNavigationProvider(options.provider);
    }
    if (options.store) {
        setStore(options.store);
    }
    applyPluginBaseOptions(options);
    Object.entries({
        MyLink,
        NavigationComponent,
        NavigationComponents
    }).forEach(([componentName, component])=>{
        instance.component(componentName, component);
    });
}
var index = {
    install
};

export { MyLink, NavigationComponent, NavigationComponents, buildNavigation, buildNavigationForTier, index as default, findNavigationElementForTier, findNavigationElementsForTier, flattenNestedNavigationElements, getActiveComponent, getComponents, getStore, install, isAbsoluteURL, isNavigationElementMatch, reduceNavigationElementsByRestriction, removeTierFromNavigationElements, resetNavigationExpansion, selectNavigationElement, setNavigationExpansion, setNavigationProvider, setStore, setStoreItem, setTierForNavigationElements, toggleNavigation, useNavigationProvider, useStore, useStoreItem };
//# sourceMappingURL=index.mjs.map
