import type {
  NavigationGuardNext,
  RouteLocationNormalized,
  Router,
  RouterHistory
} from 'vue-router'
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'

import { LOGIN_URL, NoAuth, RouteMode } from '@/enum'
import { useUserStoreWithOut } from '@/store/modules/user'
import NProgress from '@/utils/progress'

import type { AppRouteRecordRaw, MenuType } from './types'

const userStore = useUserStoreWithOut()
class RouteView {
  // 路由对象
  private router: Router | undefined = undefined

  constructor() {
    this.router = this.createRouter()
    this.beforeRouteChange()
    this.afterRouteChange()
  }

  // 根据环境变量中的配置生成路由模式
  private createHistory = (): RouterHistory => {
    if (import.meta.env.VITE_ROUTER_MODE === RouteMode.HISTORY) {
      return createWebHistory()
    } else {
      return createWebHashHistory()
    }
  }

  // 动态获取 modules 目录下的所有 .ts 文件生成基础路由
  private createBasicRoutes = (): AppRouteRecordRaw[] => {
    const moduleFiles: Record<string, { [key: string]: any }> = import.meta.glob(
      './modules/**/*.ts',
      {
        eager: true
      }
    )
    const routeModuleList: AppRouteRecordRaw[] = []
    Object.keys(moduleFiles).forEach((key) => {
      const mod: { [key: string]: any } = moduleFiles[key].default || {}
      const modList: AppRouteRecordRaw[] = Array.isArray(mod) ? [...mod] : [mod]
      routeModuleList.push(...modList)
    })

    return routeModuleList
  }

  // 创建路由对象
  private createRouter(): Router {
    return createRouter({
      history: this.createHistory(),
      routes: this.createBasicRoutes(),
      strict: true,
      scrollBehavior: () => ({ left: 0, top: 0 })
    })
  }

  // 路由守卫
  private beforeRouteChange(): void {
    if (!this.router) {
      return
    }
    const curRouter = this.router as Router
    curRouter.beforeEach((to, _, next) => {
      NProgress.start()


      // 如果网站不需要登录认证，所有路由直接切换
      if (to.meta.ignoreAuth) {
        next()
        return
      }
      if (import.meta.env.VITE_APP_NO_AUTH === NoAuth.NO_AUTH) {
        next()
      } else {
        if (this.checkLogin(to)) {
          if (this.checkPermission(to)) {
            next()
          } else {
            this.to403(next)
          }
        } else {
          this.toLogin(to, next)
        }
      }
    })
  }

  /**
   * 检查是否认证
   * @param to 需要跳转的路由
   * @returns 检查结果
   */
  private checkLogin(to: RouteLocationNormalized): boolean {
    if (to.meta.ignoreAuth) {
      return true
    } else {
      return !!userStore.userToken
    }
  }

  /**
   * 检查用户是否有访问权限
   * @param _to 需要跳转的路由
   * @returns 检查结果
   */
  private checkPermission(_to: RouteLocationNormalized): boolean {
    return true
  }

  private toLogin(to: RouteLocationNormalized, next: NavigationGuardNext): void {
    next({
      path: LOGIN_URL,
      query: {
        redirect: to.fullPath
      }
    })
  }

  private to403(next: NavigationGuardNext): void {
    next({
      name: '403'
    })
  }

  private afterRouteChange(): void {
    if (!this.router) {
      return
    }

    const myRouter = this.router as Router
    myRouter.afterEach((to) => {
      document.title = (to?.meta?.title as string) || import.meta.env.VITE_APP_TITLE
      NProgress.done()
    })
  }

  // 获取所有子路由
  private getChildRoutes(route: AppRouteRecordRaw): string[] {
    const childList: string[] = []
    if (!route.children?.length) {
      return childList
    }

    for (const item of route.children) {
      childList.push(item.path)
      childList.push(...this.getChildRoutes(item))
    }
    return childList
  }

  private getNormalRoutes(): AppRouteRecordRaw[] {
    if (!this.router) {
      return []
    }

    // 因为所有路由都是平铺结构，而且路由间的嵌套关系依然存在，因此我们要找到真正的顶层路由，根据嵌套关系显示在菜单中
    const routes = this.router?.getRoutes()
    const childRoutes: string[] = []
    for (const route of routes) {
      childRoutes.push(...this.getChildRoutes(route as unknown as AppRouteRecordRaw))
    }

    // 去重重复路径
    const noDuplicatePaths = new Set([...childRoutes])

    return routes.filter((route) => {
      return !noDuplicatePaths.has(route.path)
    }) as unknown as AppRouteRecordRaw[]
  }

  private formatRouteToMenu(routes: Optional<AppRouteRecordRaw[]>, menus: MenuType[]): void {
    if (!routes) {
      return
    }

    const menuRoutes = routes.filter((route) => {
      return !route.meta.hideInMenu
    })

    for (const item of menuRoutes) {
      const children: MenuType[] = []
      item.children && this.formatRouteToMenu(item.children, children)
      menus.push({
        name: item.name,
        title: item.meta.title,
        icon: item.meta.icon ? item.meta.icon : '',
        children: children.length ? children : undefined
      })
    }
  }

  // 生成路由菜单
  public generatorMenu() {
    const routes = this.getNormalRoutes()
    const menus: MenuType[] = []
    this.formatRouteToMenu(routes, menus)
    return menus
  }

  // 获取路由对象
  public getRouter(): Router {
    return this.router as Router
  }
}

const routeView = new RouteView()

export { MenuType, routeView }

export default routeView.getRouter()
