export class MenuItem {

    name: string;
    caption: string;
    desc?: string;
    type?: string;
    rank?: string;
    permission?: string | boolean;
    subs?: MenuItem[];
    level?: number;
    path?: string;
    permittedPath?: string[];
    visible?: boolean;

    constructor(obj?: Record<string, any>) {
        if (obj) {
            Object.assign(this, obj);
        }
    }

    isSubVisible(): boolean {
        if (this.subs && this.subs.length) {
            let visible = true;
            for (let sub of this.subs) {
                visible = (sub.visible !== false) && visible;
            }
            return visible;
        }
        return false;
    }

}

function buildLevel(items: MenuItem[], parentLevel: number): MenuItem[] {
    if (items) {
        let level = parentLevel ? (parentLevel + 1) : 1;
        for (let item of items) {
            item.level = level;
            buildLevel(item.subs, level);
        }
    }
    return items;
}

function findItemChain(path: string, items: MenuItem[], fnItemChain: (item: MenuItem, chain?: MenuItem[]) => MenuItem[]): MenuItem[] {
    for (let item of items) {
        if (matches(item, path)) {
            return fnItemChain(item);
        }
        // 直接路径不匹配，则尝试在子菜单中查找
        if (item.subs) {
            let chain = findItemChain(path, item.subs, fnItemChain);
            if (chain) {
                return fnItemChain(item, chain);
            }
        }
    }
    return [];
}

function matches(item: MenuItem, path: string): boolean {
    // 去掉可能的请求参数部分
    const index = path.indexOf('?');
    if (index >= 0) {
        path = path.substring(index);
    }
    if (item.path && matchesPath(item.path, path)) {
        return true;
    }
    if (typeof item.permission === 'string') { // 如果没有指定路径但指定了许可名
        if (item.permittedPath) { // 如果指定了许可路径，则先尝试匹配许可路径和指定路径
            for (let permittedPath of item.permittedPath) {
                if (matchesPath(permittedPath, path)) {
                    return true;
                }
            }
        }
        // 此时还未匹配，则将指定路径按照默认规则转换为许可名尝试匹配
        let permission = getDefaultPermission(path);
        return item.permission === permission;
    }
    return false;
}

function matchesPath(pathPattern: string, actualPath: string): boolean {
    let pattern = pathPattern.replace(/\/:[a-zA-Z0-9_]+/g, '/[a-zA-Z0-9_\\*]+');
    if (pattern === pathPattern) { // 无路径参数
        return pathPattern === actualPath;
    } else { // 有路径参数，正则表达式全字符串比较匹配
        return new RegExp('^' + pattern + '$', 'g').test(actualPath);
    }
}

function getDefaultPermission(path: string): string {
    // 确保路径头尾都有/
    if (!path.startsWith('/')) {
        path = '/' + path;
    }
    if (!path.endsWith('/')) {
        path += '/';
    }
    // 移除可能包含的路径变量
    let permission = path.replace(/\/:[^/]+\//g, '/').replace(/\*/g, '');
    // 去掉许可头尾的/
    if (permission.startsWith('/')) {
        permission = permission.substring(1);
    }
    if (permission.endsWith('/')) {
        permission = permission.substring(0, permission.length - 1);
    }
    // 许可所有中间的/替换为_
    permission = permission.replace(/\//g, '_');
    return permission;
}

function addMatchedItemTo(items: MenuItem[], path: string, targetItems: MenuItem[]): void {
    if (items) {
        for (let item of items) {
            if (matches(item, path)) { // 找到匹配的菜单项，则加入目标项目清单，直接返回
                targetItems.push(item);
                return;
            }
            // 不匹配则尝试比较下级菜单项
            addMatchedItemTo(item.subs, path, targetItems);
            if (targetItems.length > 0) { // 如果在下级菜单中找到匹配，则当前级别需要插入到目标清单的首位
                targetItems.unshift(item);
                return;
            }
        }
    }
}

function findItemByPermission(app: string, items: MenuItem[], permission: string): MenuItem | null {
    for (let item of items) {
        prepareItem(item);
        if (item.permission === permission) {
            return item;
        }
        if (app && (app + '.' + item.permission) === permission) {
            return item;
        }
        if (item.subs) {
            const sub = findItemByPermission(app, item.subs, permission);
            if (sub) {
                return sub;
            }
        }
    }
    return null;
}

function prepareItem(item: MenuItem) {
    if (item.path && item.permission === true) {
        item.permission = getDefaultPermission(item.path);
    }
}

function findAssignableItems(items: MenuItem[]): MenuItem[] {
    const assignableItems: MenuItem[] = [];
    items.forEach(item => {
        let assignableItem = new MenuItem({
            subs: [],
        });
        prepareItem(item);
        if (item.subs && item.subs.length) {
            assignableItem.subs = findAssignableItems(item.subs);
        }
        // 当前菜单有许可限定，或有可分配的子级或操作，则当前菜单项为可分配项
        if (item.permission || assignableItem.subs.length) {
            assignableItem = Object.assign({}, item, assignableItem);
            assignableItems.push(assignableItem);
        }
    });
    return assignableItems;
}

export default class Menu {

    ordinal = 0;
    app = '';
    name: string;
    caption: string;
    items: MenuItem[];

    constructor(obj?: Record<string, any>) {
        if (obj) {
            Object.assign(this, obj);
            this.setItems(obj.items);
        }
    }

    setItems(items: MenuItem[]): void {
        this.items = buildLevel(items, 0);
    }

    getItemByPath(path: string): MenuItem | null {
        let chain = findItemChain(path, this.items, (item: MenuItem, subs): MenuItem[] => {
            return subs || [item];
        });
        return chain[0] || null;
    }

    getBreadcrumbItems(path: string) {
        return findItemChain(path, this.items, (item: MenuItem, breadcrumbItems: MenuItem[]): MenuItem[] => {
            if (breadcrumbItems && breadcrumbItems.length) {
                breadcrumbItems.unshift(item);
                return breadcrumbItems;
            } else {
                return [item];
            }
        });
    }

    findBelongingItem(path: string, level: number = 2): MenuItem | null {
        let items = [];
        addMatchedItemTo(this.items, path, items);
        // 从后往前遍历结果清单，以便于取到级别不高于目标级别的菜单项
        for (let i = items.length - 1; i >= 0; i--) {
            let item = items[i];
            if (item.level <= level && item.visible !== false) {
                let parentItem = items[i - 1];
                if (!parentItem || parentItem.isSubVisible()) {
                    return item;
                }
            }
        }
        return null;
    }

    getItemByPermission(permission: string): MenuItem | null {
        return findItemByPermission(this.app, this.items, permission);
    }

    getAssignableItems() {
        return findAssignableItems(this.items);
    }

}
