import { defineStore } from "pinia";

export const useNavigationStore = defineStore("navigation", {
    state: () => ({
        routeState: {
            alt: "Home",
            href: "/cgi-bin/koha/mainpage.pl",
            is_navigation_item: false,
            is_base: true,
            children: [],
        },
        current: null,
        params: {},
    }),
    actions: {
        setRoutes(routesDef) {
            if (!Array.isArray(routesDef)) {
                routesDef = [routesDef];
            }
            this.routeState.children = routesDef;
            _traverseChildren(this.routeState);

            return this.navigationRoutes;

            // Function declarations

            function _traverseChildren(parent) {
                if (isParent(parent)) {
                    parent.children.forEach(child => {
                        _setChildDefaults(parent, child);
                        _traverseChildren(child);
                    });
                }
            }

            function _setChildDefaults(parent, child) {
                child.parent = parent;
                if (isRoutable(child)) {
                    _setMetadata(child);
                }
                if (parent.children.length === 1 && parent.is_base) {
                    _setBaseAndNavigationDefaults(child, {
                        is_base: true,
                        is_navigation_item: false,
                    });
                } else {
                    _setBaseAndNavigationDefaults(child, {
                        is_base: false,
                        is_navigation_item: true,
                    });
                }
            }

            function _setBaseAndNavigationDefaults(
                child,
                { is_base, is_navigation_item }
            ) {
                child.is_base =
                    child.is_base !== undefined ? child.is_base : is_base;
                child.is_navigation_item =
                    child.is_navigation_item !== undefined
                        ? child.is_navigation_item
                        : is_navigation_item;
            }

            function _setMetadata(child) {
                if (!child.meta) child.meta = {};
                child.meta.self = child;
            }
        },
    },
    getters: {
        breadcrumbs() {
            if (this.current)
                return _buildFromCurrentMatches(
                    this.current,
                    this.routeState,
                    this.params
                );

            return _getBaseElements(this.routeState);

            // Function declarations

            function _getBaseElements(parent) {
                if (!parent.is_base) return [];
                let next = {};
                if (isParent(parent)) {
                    next = _defineNextElement(parent);
                }
                return [
                    {
                        ...parent,
                        children: null,
                    },
                    ..._getBaseElements(next),
                ];
            }

            function _defineNextElement(parent) {
                return (
                    parent.children.find(child => child.is_default) ||
                    parent.children[0]
                );
            }

            function _buildFromCurrentMatches(
                currentMatches,
                routeState,
                params
            ) {
                return [
                    {
                        ...routeState,
                        icon: null,
                        children: null,
                    },
                    ..._mapMatches(currentMatches, params),
                ];
            }

            function _isBaseOrNotStub(child) {
                return child.is_base || (child.path && child.path !== "");
            }

            function _isEmptyNode(child) {
                return !child.is_empty;
            }

            function _getPath(match, params) {
                if (!match.name && match.path && params) {
                    return match.path.replaceAll(/(:[^/]+)/g, param_name => {
                        return params[param_name.substr(1)];
                    });
                }
                return match.path;
            }

            function _mapMatches(currentMatches, params) {
                return currentMatches
                    .filter(match => _isBaseOrNotStub(match.meta.self))
                    .filter(match => _isEmptyNode(match.meta.self))
                    .map(match => {
                        let path = _getPath(match, params);
                        return {
                            ...match.meta.self,
                            icon: null,
                            path,
                            children: null,
                        };
                    });
            }
        },
        leftNavigation() {
            return _getNavigationElements(this.routeState);

            // Function declarations

            function _getNavigationElements(parent, prevPath = "") {
                if (_isBaseAndNoChildren(parent)) return [];
                if (parent.is_base)
                    return _buildChildNavigationElements(parent).flat(Infinity);

                const builtPath = _buildPath(prevPath, parent);

                let children = [];
                if (!parent.is_end_node && isParent(parent)) {
                    children = _buildChildNavigationElements(parent, builtPath);
                }

                return {
                    ...parent,
                    path: builtPath ? builtPath : parent.path,
                    children,
                };
            }

            function _buildPath(prevPath, element) {
                let builtPath;

                if (isRoutable(element) && isAbsolutePath(element.path)) {
                    builtPath = element.path;
                } else {
                    if (prevPath)
                        builtPath =
                            "" + prevPath + addSlashIfNotPresent(prevPath);
                    if (isRoutable(element))
                        builtPath = "" + builtPath + element.path;
                }

                return builtPath;
            }

            function _buildChildNavigationElements(parent, builtPath) {
                return parent.children
                    .filter(child => child.is_base || child.is_navigation_item)
                    .map(child => _getNavigationElements(child, builtPath));
            }

            function _isBaseAndNoChildren(parent) {
                return (
                    parent.is_base &&
                    (!parent.children || !parent.children.length)
                );
            }
        },
        navigationRoutes() {
            let routes = _toRoute(this.routeState);
            return Array.isArray(routes) ? routes : [routes];

            // Function declarations

            function _toRoute(parent) {
                if (!isRoutable(parent)) return _getRoutableChildren(parent);
                return parent;
            }

            function _getRoutableChildren(parent) {
                return parent.children
                    .map(child => _toRoute(child))
                    .flat(Infinity);
            }
        },
    },
});

function addSlashIfNotPresent(path) {
    return /\/$/.test(path) ? "" : "/";
}

function isRoutable(element) {
    return element.path !== undefined || element.name !== undefined;
}

function isParent(parent) {
    return parent.children && parent.children.length;
}

function isAbsolutePath(path) {
    return /^\//.test(path);
}
