import type {
  LocationQueryRaw,
  NavigationGuardNext,
  RouteLocationNormalized,
  RouteLocationRaw,
  Router
} from 'vue-router';
import type { RouteKey, RoutePath } from '@elegant-router/types';
import { useAuthStore } from '@/store/modules/auth';
import { useObjectContextStore } from '@/store/modules/object-context';
import { useRouteStore } from '@/store/modules/route';
import { localStg } from '@/utils/storage';
import { getRouteName } from '@/router/elegant/transform';

/**
 * 创建全局路由守卫
 *
 * @param router 路由实例
 */
export function createRouteGuard(router: Router) {
  router.beforeEach(async (to, from, next) => {
    const location = await initRoute(to);

    if (location) {
      next(location);
      return;
    }

    const authStore = useAuthStore();

    const rootRoute: RouteKey = 'root';
    const loginRoute: RouteKey = 'login';
    const noAuthorizationRoute: RouteKey = '403';

    const isLogin = Boolean(localStg.get('token'));
    const needLogin = !to.meta.constant;
    const routeRoles = to.meta.roles || [];

    const hasRole = authStore.userInfo.roles.some(role => routeRoles.includes(role));
    const hasAuth = authStore.isStaticSuper || !routeRoles.length || hasRole;

    // 已登录状态下访问登录页时，直接跳到首页
    if (to.name === loginRoute && isLogin) {
      const redirect = to.query?.redirect as string;
      if (redirect) {
        next({ path: redirect });
      } else {
        next({ name: rootRoute });
      }
      return;
    }

    // 不需要登录的路由允许直接访问
    if (!needLogin) {
      const objectContextLocation = await handleObjectContextSwitch(to);

      if (objectContextLocation) {
        next(objectContextLocation);
        return;
      }

      handleRouteSwitch(to, from, next);
      return;
    }

    // 需要登录但当前未登录时，跳转到登录页
    if (!isLogin) {
      next({ name: loginRoute, query: { redirect: to.fullPath } });
      return;
    }

    // 已登录但没有权限时，跳转到 403 页面
    if (!hasAuth) {
      next({ name: noAuthorizationRoute });
      return;
    }

    // 正常放行
    const objectContextLocation = await handleObjectContextSwitch(to);

    if (objectContextLocation) {
      next(objectContextLocation);
      return;
    }

    handleRouteSwitch(to, from, next);
  });
}

/**
 * 初始化路由
 *
 * @param to 目标路由
 */
async function initRoute(to: RouteLocationNormalized): Promise<RouteLocationRaw | null> {
  const routeStore = useRouteStore();

  const notFoundRoute: RouteKey = 'not-found';
  const isNotFoundRoute = to.name === notFoundRoute;

  // 常量路由还没初始化时，先初始化常量路由
  if (!routeStore.isInitConstantRoute) {
    await routeStore.initConstantRoute();

    // 初始化前可能先被 not-found 捕获，初始化完成后重新回到原目标路由
    const path = to.fullPath;
    const location: RouteLocationRaw = {
      path,
      replace: true,
      query: to.query,
      hash: to.hash
    };

    return location;
  }

  const isLogin = Boolean(localStg.get('token'));

  if (!isLogin) {
    // 未登录时，如果访问的是常量路由且不是 not-found，则允许访问
    if (to.meta.constant && !isNotFoundRoute) {
      routeStore.onRouteSwitchWhenNotLoggedIn();

      return null;
    }

    // 未登录时跳到登录页
    const loginRoute: RouteKey = 'login';
    const query = getRouteQueryOfLoginRoute(to, routeStore.routeHome);

    const location: RouteLocationRaw = {
      name: loginRoute,
      query
    };

    return location;
  }

  if (!routeStore.isInitAuthRoute) {
    // 初始化权限路由
    await routeStore.initAuthRoute();

    // 初始化前可能先被 not-found 捕获，初始化完成后重新回到原目标路由
    if (isNotFoundRoute) {
      const rootRoute: RouteKey = 'root';
      const path = to.redirectedFrom?.name === rootRoute ? '/' : to.fullPath;

      const location: RouteLocationRaw = {
        path,
        replace: true,
        query: to.query,
        hash: to.hash
      };

      return location;
    }
  }

  routeStore.onRouteSwitchWhenLoggedIn();

  // 权限路由已初始化，且当前不是 not-found，直接放行
  if (!isNotFoundRoute) {
    return null;
  }

  // 如果被 not-found 捕获，再判断是否其实是“存在但无权限”的路由
  const exist = await routeStore.getIsAuthRouteExist(to.path as RoutePath);
  const noPermissionRoute: RouteKey = '403';

  if (exist) {
    const location: RouteLocationRaw = {
      name: noPermissionRoute
    };

    return location;
  }

  return null;
}

function handleRouteSwitch(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) {
  // 带 href 的路由直接新开窗口
  if (to.meta.href) {
    window.open(to.meta.href, '_blank');

    next({ path: from.fullPath, replace: true, query: from.query, hash: from.hash });

    return;
  }

  next();
}

async function handleObjectContextSwitch(to: RouteLocationNormalized) {
  const objectContextStore = useObjectContextStore();

  return objectContextStore.ensureContextByRoute(to);
}

function getRouteQueryOfLoginRoute(to: RouteLocationNormalized, routeHome: RouteKey) {
  const loginRoute: RouteKey = 'login';
  const redirect = to.fullPath;
  const [redirectPath, redirectQuery] = redirect.split('?');
  const redirectName = getRouteName(redirectPath as RoutePath);

  const isRedirectHome = routeHome === redirectName;

  const query: LocationQueryRaw = to.name !== loginRoute && !isRedirectHome ? { redirect } : {};

  if (isRedirectHome && redirectQuery) {
    query.redirect = `/?${redirectQuery}`;
  }

  return query;
}
