/**
 * 基于Vue的路由器构建函数
 */
import {
    createRouter,
    createWebHashHistory,
    Router,
    RouteRecordRaw,
    RouterHistory,
    RouteLocationNormalized,
    RouteLocationNormalizedLoaded,
    RouteLocationNormalizedLoadedGeneric,
    NavigationGuardNext,
} from 'vue-router';
import * as NetUtil from '../../tnxcore/src/util/net.ts';

export type RouteItem = {
    caption: string;
    path: string;
    icon?: string;
    page?: string;
    component?: () => Promise<any>;
    redirect?: never;
    alias?: string;
    subs?: RouteItem[];
}

function addRoute(routes: RouteRecordRaw[], superiorPath: string, item: RouteItem, fnImportPage?: (page: string) => Promise<any>): void {
    if (item && item.path) {
        let page = item.page || item.path.replace(/\/:[a-zA-Z0-9_]+/g, '');
        let route: RouteRecordRaw = {
            path: item.path,
            meta: {
                superiorPath: superiorPath,
                page: page,
                cache: {}, // 路由级缓存
                isHistory() { // 通过setTimeout()方式调用才能确保获得正确结果
                    return this.historyFrom !== undefined;
                },
            },
            component: item.component,
        };
        if (!route.component && fnImportPage) {
            route.component = () => {
                return fnImportPage(page);
            };
        }
        // 如果直接定义route的redirect/alias字段，则item的redirect/alias为undefined时，route仍然有redirect/alias字段，只是其值为undefined，这将导致VueRouter报错
        if (item.redirect) {
            route.redirect = item.redirect;
        }
        if (item.alias) {
            route.alias = item.alias;
        }
        routes.push(route);
    }
}

function applyItemsToRoutes(superiorPath: string, items: RouteItem[], routes: RouteRecordRaw[], fnImportPage?: (page: string) => Promise<any>): void {
    if (items && items.length) {
        items.forEach(item => {
            if (item) {
                addRoute(routes, superiorPath, item, fnImportPage);
                applyItemsToRoutes(item.path, item.subs, routes, fnImportPage);
            }
        });
    }
}

function instantiatePath(path: string, params?: Record<string, any>): string {
    if (path && path.includes('/:')) {
        if (params) {
            Object.keys(params).forEach(key => {
                path = path.replace('/:' + key + '/', '/' + params[key] + '/');
            });
        }
        if (path.includes('/:')) { // 参数替换完之后，还有路径参数，则为无效路径，返回首页
            console.warn(
                '路径中的参数无法获得参数值，请确保具有参数的路径所属菜单项的下级菜单路径包含相同的参数：' + path);
            return '/';
        }
    }
    return path;
}

function getCurrentRoute(router: Router): RouteLocationNormalizedLoadedGeneric {
    return router.currentRoute.value;
}

export type VueRouter = Router & {
    history: RouterHistory;
    prev?: RouteLocationNormalizedLoaded;
    $beforeLeaveHandlers: Record<string, (to: RouteLocationNormalized) => boolean>;
    beforeLeave: (handler: (to: RouteLocationNormalized) => boolean) => void;
    pushState: (path: string) => boolean;
    replaceState: (path: string) => boolean;
    backTo: (path?: string) => void;
}

export default function (items: RouteItem[], fnImportPage?: (page: string) => Promise<any>): VueRouter {
    const routes: RouteRecordRaw[] = [];
    applyItemsToRoutes('', items, routes, fnImportPage);

    const routerHistory = createWebHashHistory();
    const router: VueRouter = createRouter({
        history: routerHistory,
        routes,
    }) as VueRouter;
    router.history = routerHistory;

    // 浏览器的返回事件触发位于VueRouter的钩子执行和页面渲染之后，这意味着$route.meta.historyFrom必须在页面渲染完之后才具有正确的值
    // if (window.history && window.history.pushState) {
    //     window.history.pushState(null, null, document.URL);
    // }
    window.addEventListener('popstate', () => {
        let $route = getCurrentRoute(router);
        if ($route) {
            $route.meta.historyFrom = router.history.state.forward;
        }
    }, false);

    // 注册离开页面前事件处理支持
    router.$beforeLeaveHandlers = {};
    router.beforeLeave = function (handler: (to: RouteLocationNormalized) => boolean): void {
        let $route = getCurrentRoute(router);
        let path = $route.path;
        router.$beforeLeaveHandlers[path] = handler;
    };

    router.beforeEach((to: RouteLocationNormalized, from: RouteLocationNormalizedLoaded, next: NavigationGuardNext): void => {
        let allow = true;
        let beforeLeaveHandler = router.$beforeLeaveHandlers[from.path];
        if (beforeLeaveHandler) {
            if (beforeLeaveHandler(to) === false) {
                allow = false;
            }
        }
        if (allow) {
            next();
        }
    });

    router.afterEach((to: RouteLocationNormalized, from: RouteLocationNormalizedLoaded): void => {
        router.prev = from;
        // 前后路径相同，但全路径不同（意味着参数不同），则需要刷新页面，否则页面不会刷新
        if (to.path === from.path && to.fullPath !== from.fullPath) {
            window.location.reload();
        }
    });

    router.backTo = function (path?: string): void {
        if (!router.prev?.path) { // 没有path，说明当前页面为刷新后进入的第一个页面，无法简单返回
            let $route = getCurrentRoute(router);
            if (!path) { // 未指定默认返回路径，则返回上一级页面
                path = $route.meta.superiorPath as string;
            }
            path = instantiatePath(path, $route.params);
            if (path) {
                router.replace(path);
                return;
            }
        }
        router.back.call(router);
    }

    router.pushState = function (path: string): boolean {
        let success = NetUtil.pushState('#' + path);
        if (!success) {
            this.push(path);
        }
        return success;
    };

    router.replaceState = function (path: string): boolean {
        let success = NetUtil.replaceState('#' + path);
        if (!success) {
            this.replace(path);
        }
        return success;
    };

    return router;
}
