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

/**
 * 创建路由守卫
 *
 * @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) {
      next({ name: rootRoute })
      return
    }

    // 如果路由不需要登录，则直接允许访问
    if (!needLogin) {
      handleRouteSwitch(to, from, next)
      return
    }

    // 如果路由需要登录但用户未登录，则跳转到登录页
    const isAuthRouteForceLogin = import.meta.env.VITE_AUTH_ROUTE_FORCE_LOGIN === 'Y'
    const whiteList: string[] = ['user']
    const isInWhiteList = whiteList.includes(to.name as string)

    if (isAuthRouteForceLogin && !isLogin && !isInWhiteList) {
      next({ name: loginRoute, query: { redirect: to.fullPath } })
      return
    }

    // 如果用户已登录但没有权限，则跳转到 403 页面
    if (!hasAuth) {
      next({ name: noAuthorizationRoute })
      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()

    // 由于常量路由未初始化，路由被“未找到”路由捕获
    // 常量路由初始化后，重定向到原始路由
    const path = to.fullPath
    const location: RouteLocationRaw = {
      path,
      replace: true,
      query: to.query,
      hash: to.hash,
    }

    return location
  }

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

  const isAuthRouteForceLogin = import.meta.env.VITE_AUTH_ROUTE_FORCE_LOGIN === 'Y'
  const whiteList: string[] = ['user']
  const isInWhiteList = whiteList.includes(to.name as string)

  if (isAuthRouteForceLogin && !isLogin && !isInWhiteList) {
    // 如果用户未登录且路由是常量路由但不是“未找到”路由，则允许访问。
    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()

    // 由于权限路由未初始化，路由被“未找到”路由捕获
    // 权限路由初始化后，重定向到原始路由
    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()

  // 权限路由已初始化
  // 如果不是“未找到”路由，则允许访问
  if (!isNotFoundRoute) {
    return null
  }

  // 如果被“未找到”路由捕获，则检查路由是否存在
  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) {
  // 外链路由
  if (to.meta.href) {
    window.open(to.meta.href, '_blank')

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

    return
  }

  next()
}

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
}
