import type { RouteLocationNormalized, Router } from 'vue-router'
import { useAppStoreWithOut } from '/@/store/modules/app'
import { useUserStoreWithOut } from '/@/store/modules/user'
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'
import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel'
import { Modal, notification } from 'ant-design-vue'
import { warn } from '/@/utils/log'
import { unref } from 'vue'
import { setRouteChange } from '/@/logics/mitt/routeChange'
import nProgress from 'nprogress'
import { useFullscreen } from '@vueuse/core'
import { createPermissionGuard } from './permissionGuard'
import { createStateGuard } from './stateGuard'
import projectSetting from '/@/settings/projectSetting'
import { createParamMenuGuard } from './paramMenuGuard'

// Don't change the order of creation
export function setupRouterGuard(router: Router) {
    createPageGuard(router)
    createPageLoadingGuard(router)
    createHttpGuard(router)
    createScrollGuard(router)
    createMessageGuard(router)
    createProgressGuard(router)
    createPermissionGuard(router)
    createParamMenuGuard(router) // must after createPermissionGuard (menu has been built.)
    createStateGuard(router)
    createAutoFullScreenGuard(router)
}

/**
 * Hooks for handling page state
 */
function createPageGuard(router: Router) {
    const loadedPageMap = new Map<string, boolean>()

    router.beforeEach(async (to) => {
        // The page has already been loaded, it will be faster to open it again, you don’t need to do loading and other processing
        to.meta.loaded = !!loadedPageMap.get(to.path)
        // Notify routing changes
        setRouteChange(to)

        return true
    })

    router.afterEach((to) => {
        loadedPageMap.set(to.path, true)
    })
}

// Used to handle page loading status
function createPageLoadingGuard(router: Router) {
    const userStore = useUserStoreWithOut()
    const appStore = useAppStoreWithOut()
    const { getOpenPageLoading } = useTransitionSetting()
    router.beforeEach(async (to) => {
        if (!userStore.getToken) {
            return true
        }
        if (to.meta.loaded) {
            return true
        }

        if (unref(getOpenPageLoading)) {
            appStore.setPageLoadingAction(true)
            return true
        }

        return true
    })
    router.afterEach(async () => {
        if (unref(getOpenPageLoading)) {
            // TODO Looking for a better way
            // The timer simulates the loading time to prevent flashing too fast,
            setTimeout(() => {
                appStore.setPageLoading(false)
            }, 220)
        }
        return true
    })
}

/**
 * The interface used to close the current page to complete the request when the route is switched
 * @param router
 */
function createHttpGuard(router: Router) {
    const { removeAllHttpPending } = projectSetting
    let axiosCanceler: Nullable<AxiosCanceler>
    if (removeAllHttpPending) {
        axiosCanceler = new AxiosCanceler()
    }
    router.beforeEach(async () => {
        // Switching the route will delete the previous request
        axiosCanceler?.removeAllPending()
        return true
    })
}

// Routing switch back to the top
function createScrollGuard(router: Router) {
    const isHash = (href: string) => {
        return href.startsWith('#')
    }

    const body = document.body

    router.afterEach(async (to) => {
        // scroll top
        isHash((to as RouteLocationNormalized & { href: string })?.href) && body.scrollTo(0, 0)
        return true
    })
}

/**
 * Used to close the message instance when the route is switched
 * @param router
 */
export function createMessageGuard(router: Router) {
    const { closeMessageOnSwitch } = projectSetting

    router.beforeEach(async () => {
        try {
            if (closeMessageOnSwitch) {
                Modal.destroyAll()
                notification.destroy()
            }
        } catch (error) {
            warn(`message guard error:${error}`)
        }
        return true
    })
}

export function createProgressGuard(router: Router) {
    const { getOpenNProgress } = useTransitionSetting()
    router.beforeEach(async (to) => {
        if (to.meta.loaded) {
            return true
        }
        unref(getOpenNProgress) && nProgress.start()
        return true
    })

    router.afterEach(async () => {
        unref(getOpenNProgress) && nProgress.done()
        return true
    })
}

export function createAutoFullScreenGuard(router: Router) {
    const { enter, exit, isFullscreen } = useFullscreen()

    router.beforeEach(async (to) => {
        if (to.meta.autoFullScreen) {
            !isFullscreen.value && enter()
        }

        return true
    })

    router.afterEach(async (to) => {
        if (!to.meta.autoFullScreen) {
            isFullscreen.value && exit()
        }

        return true
    })
}
