import { ref, toRefs } from 'vue'
import { createRouter, RouterOptions, RouteRecordRaw, RouteLocationNormalized } from 'vue-router'

interface HistoryRoute {
    name: string;
    fullPath: string;
}

interface HistoryState {
    historyRoutes: HistoryRoute[]; // 历史路由列表
    excludeViews: string[]; // 不缓存的视图
    actionName: '' | 'push' | 'replace' | 'forward' | 'back'; // 当前路由动作
    transitionName: '' | 'route-out' | 'route-in'; // 前进后退动画
    goStep: number; // 前进后退步数
}

export default new (class {
    private storageKey = 'history_mobile'

    private state = ref<HistoryState>({
        historyRoutes: [],
        excludeViews: [],
        actionName: '',
        transitionName: '',
        goStep: 0
    })

    constructor() {
        const state = sessionStorage.getItem(this.storageKey)
        if (state) {
            // 合并赋值，防止 sessionStorage 获取的值有问题
            this.state.value = {
                ...this.state.value,
                ...JSON.parse(state)
            }
        }
    }

    getState() {
        return this.state.value
    }

    getStateRef() {
        return toRefs(this.state.value)
    }

    /**
     * 创建路由
     * @param options 
     * @returns 
     */
    create = (options: RouterOptions) => {
        const router = createRouter(options)
        const { push, replace, go, forward, back } = router
        const { excludeViews, actionName, goStep } = this.getStateRef()

        // 添加
        router.push = (to: RouteRecordRaw) => {
            actionName.value = 'push'
            return push(to)
        }

        // 替换
        router.replace = (to: RouteRecordRaw) => {
            actionName.value = 'replace'
            return replace(to)
        }

        // 前进后退
        router.go = (delta: number) => {
            goStep.value = delta
            if (delta > 0) {
                actionName.value = 'forward'
            }
            if (delta < 0) {
                actionName.value = 'back'
            }
            go(delta)
        }

        // 前进
        router.forward = () => {
            actionName.value = 'forward'
            forward()
        }

        // 后退
        router.back = () => {
            actionName.value = 'back'
            back()
        }

        router.beforeResolve((to) => {
            this.addHistory(to)
        })

        router.afterEach(() => {
            excludeViews.value = []
        })

        return router
    }

    /**
     * 添加历史记录
     * @param route 
     */
    private addHistory = (route: RouteLocationNormalized) => {
        const { historyRoutes, excludeViews, actionName, transitionName, goStep } = this.getStateRef()
        const newRoute = {
            name: route.name as string,
            fullPath: route.fullPath
        }

        // 如果是替换动作，必定是前进
        if (actionName.value === 'replace') {
            const lastIndex = historyRoutes.value.length - 1
            const lastRecord = historyRoutes.value[lastIndex]

            if (lastRecord) {
                excludeViews.value.push(lastRecord.name as string)
                historyRoutes.value[lastIndex] = newRoute // 更新最后一条记录
            } else {
                historyRoutes.value.push(newRoute)
            }

            transitionName.value = 'route-in' // 前进动画
        } else {
            // 倒序查找路由所在的位置
            const index = (() => {
                for (let i = historyRoutes.value.length - 1; i >= 0; i--) {
                    if (historyRoutes.value[i].fullPath === route.fullPath) {
                        return i
                    }
                }
                return -1
            })()

            if (index > -1) {
                if (actionName.value === 'push') {
                    historyRoutes.value.push(newRoute)
                    transitionName.value = 'route-in' //前进动画
                } else {
                    if (historyRoutes.value.length > 1) {
                        const i = index + 1
                        const n = historyRoutes.value.length - i

                        excludeViews.value = historyRoutes.value.map((e) => e.name).slice(-n) // 返回数组最后位置开始的n个元素
                        historyRoutes.value.splice(i, n) // 从i位置开始删除后面所有元素(包括i)
                    }
                    transitionName.value = 'route-out' //后退动画
                }
            } else {
                if (goStep.value < 0) {
                    const i = historyRoutes.value.length + goStep.value
                    if (i > 0) {
                        const n = historyRoutes.value.length - i
                        excludeViews.value = historyRoutes.value.map((e) => e.name).slice(-n) // 返回数组最后位置开始的n个元素
                        historyRoutes.value.splice(i)
                    }
                }
                // 如有存在重定向的页面，判断路由前进还是后退
                if (route.redirectedFrom) {
                    if (goStep.value < 0) {
                        const index = historyRoutes.value.length - 1
                        const lastIndex = index > -1 ? index : 0
                        historyRoutes.value[lastIndex] = newRoute // 更新最后一条记录
                        transitionName.value = 'route-out' //后退动画
                    } else {
                        const [lastRecord] = historyRoutes.value.slice(-1)
                        if (route.redirectedFrom.fullPath !== lastRecord?.fullPath) {
                            historyRoutes.value.push(newRoute)
                        }
                        transitionName.value = 'route-in' // 前进动画
                    }
                } else {
                    historyRoutes.value.push(newRoute)
                    if (historyRoutes.value.length > 1) {
                        transitionName.value = 'route-in' // 前进动画
                    }
                }
            }
        }

        actionName.value = ''
        goStep.value = 0

        // 处理对象循环引用
        // const getCircularReplacer = () => {
        //     const seen = new WeakSet()
        //     return (key: string, value: unknown) => {
        //         if (typeof value === 'object' && value !== null) {
        //             if (seen.has(value)) {
        //                 return
        //             }
        //             seen.add(value)
        //         }
        //         return value
        //     }
        // }

        sessionStorage.setItem(this.storageKey, JSON.stringify(this.state.value))
    }
})