import {onLoad} from "@dcloudio/uni-app"
import {computed, onUnmounted, readonly, ref, nextTick} from "vue"
import {array, empty, object, string} from "@/uni_modules/uv-ui-tools/libs/function/test";
import mxConst, {getTabRoutes} from "@/common/mxConst";
import {guid, sleep} from "@/uni_modules/uv-ui-tools/libs/function";
import baseConfig from "@/config";
import mxConfig from "@/common/mxConfig";
import {useH5BackHome} from "@/components/mx-nav-bar/useH5BackHome";
import {createEventHook, injectLocal, provideLocal} from "@vueuse/core";
import _ from 'lodash';

export const useTransfer = function () {
    // data
    const cacheKey = ref('')
    const prevData = ref({})
    const usingCacheTransfer = computed(() => !!cacheKey.value)
    // 回传事件，只要在transferTo中定义一个callback字符串标识即可响应回调事件
    // 用uni.$on/$off/$emit来实现，如果要通用的话，可以考虑自行实现一个全局的eventBus
    const pageCallback = createEventHook() // 使用hook给transfer外部用户提供钩子
    const eventName = ref('') // 给发起跳转的页面记录事件注册, 用于自动注销
    const callbackEventData = ref(null) // 给当前页面存放数据，用于回传

    // hooks
    onLoad((opt) => {
        if (opt.cacheKey) cacheKey.value = opt.cacheKey, prevData.value = uni.getStorageSync(opt.cacheKey)
        else prevData.value = JSON.parse(decodeURIComponent(opt.data || '{}'))
    })
    onUnmounted(() => {
        if (usingCacheTransfer.value) uni.removeStorageSync(cacheKey.value)
        // 发起跳转的页面注销事件
        if (eventName.value) uni.$off(eventName.value)
    })

    // methods

    /*
    * main function for route, it keeps params type and supports big data transfer.
    * Note: if you defined `callback` in params, 2 pages will auto support onPageCallback event.
    * @param {String|Object} urlOrOptions - route url or options
    * @param {Object} props - indicates props to pickup from source data
    * @param {Array} source - source data array, default is [prevData.value]
    * @param {Boolean} usingCache - whether to use cache, default is false, for big data transfer
    * */
    const transferTo = function (urlOrOptions, props = {}, source = null, usingCache = false) {
        const options = object(urlOrOptions) ? urlOrOptions
            : {url: urlOrOptions, type: getTabRoutes().includes(urlOrOptions) ? 'tab' : 'to'}

        // confirm params
        let params = options.params || {}
        let extra = {}
        // prevData 自动包含是名学测评系统的逻辑，暂时不用去掉，以免引起一些数值覆盖的问题
        const extraSource = [/*prevData.value*/].concat(source)
        if (array(props)) extra = _.merge({}, ..._.map(extraSource, obj => _.pick(obj, props)))
        else if (object(props)) extra = _.merge(props, ..._.map(extraSource, obj => _.pick(obj, _.keys(props))))
        params = _.merge(params, extra)

        // handle params, using JSON.stringify or uni.setStorageSync
        let formatParams = params
        if (!empty(params)) {
            if (usingCache) {
                const storageKey = mxConst.keyCacheUniquePrefix + guid()
                uni.setStorageSync(storageKey, params)
                formatParams = {cacheKey: storageKey}
            } else {
                formatParams = {data: encodeURIComponent(JSON.stringify(params))}
            }
        }

        // page callback event // 注意这是在发起跳转的页面监听
        if (string(params.callback) && params.callback) {
            eventName.value = params.callback
            uni.$off(eventName.value) // 先清除其它注册，只注册一次
            uni.$on(eventName.value, handleCallback)
        }

        // 这里并没有干涉其它参数，所以$uv.route支持的功能这里应该都能用
        options.params = formatParams
        uni.$uv.route(options)
        // console.log('transferTo:', options, params)
    }

    const handleCallback = async function (event) {
        await pageCallback.trigger(event)
    }

    const transferBack = function (options = {type: 'back'}) {
        // 主动调用transferBack的地方自动处理回调
        if (prevData.value.callback) {
            // 这里做了一个延迟处理，让回调发生在页面跳转之后，
            // 这样的话，页面因为数据变化引起的滚动条重置问题看起来平滑一些。
            // 如entry-single页面，换选学校之后滚动条由于缓存机制会停在选择学校之前的位置，
            // 即使设置了scrollTop也不起作用，这里延迟可以保证重置行为发生在滚动条缓存机制之后。
            const e = prevData.value.callback, arg = callbackEventData.value
            sleep().then(() => uni.$emit(e, arg))
        }
        const {hasPreviousPage} = useH5BackHome()
        if (hasPreviousPage.value) transferTo(options)
        else relaunch()
    }

    const transferToIndex = function () {
        transferTo(mxConst.routes.index)
    }

    const relaunch = function () {
        transferTo({...mxConst.routes.index, type: 'launch'})
    }

    const transferToWebView = function (url, title = '') {
        const nextPage = '/pagesOther/pages/h5/h5'
        transferTo(nextPage, {url, title})
    }

    const transferToProtocolUser = function () {
        const path = baseConfig.inlineSiteBaseUrl + mxConfig.inlineSiteFunctionPaths.protocolUser
        transferToWebView(path, '用户协议')
    }

    const transferToProtocolPrivacy = function () {
        const path = baseConfig.inlineSiteBaseUrl + mxConfig.inlineSiteFunctionPaths.protocolPrivacy
        transferToWebView(path, '隐私协议')
    }

    const transferToFAQ = function () {
        const path = baseConfig.inlineSiteBaseUrl + mxConfig.inlineSiteFunctionPaths.FAQ
        transferToWebView(path, '常见问题')
    }

    const playVideo = function (idOrSrc, aliIdType, title, callback = false) {
        transferTo(mxConst.routes.videoPlay, {aliId: idOrSrc, aliIdType, title, callback})
    }

    /*
    * tool function for clean all transfer cache data,
    * 这里提供了一个清除所有缓存数据的工具函数，
    * 可以用在一些管理性功能页中，如果页面非正常中断可能会累积一些缓存，
    * 这个函数可以用来清除所有缓存数据。
    * 注意：这个函数只是清除 transferTo 中 usingCache=true 产生缓存数据.
    * */
    const cleanAllTransferCacheData = function () {
        // 根据mxConst.keyCacheUniquePrefix清除所有缓存
        // 提供给某些管理性功能页使用，如果页面非正常中断可能会累积一些缓存
        const keys = uni.getStorageInfoSync().keys
        keys.forEach(key => {
            if (key.startsWith(mxConst.keyCacheUniquePrefix))
                uni.removeStorageSync(key)
        })
    }

    return {
        cacheKey: readonly(cacheKey), // 安全考虑，不允许外部修改
        prevData,
        usingCacheTransfer,
        eventName: readonly(eventName),
        onPageCallback: pageCallback.on,
        callbackEventData,
        transferTo,
        transferBack,
        transferToIndex,
        relaunch,
        transferToWebView,
        transferToProtocolUser,
        transferToProtocolPrivacy,
        transferToFAQ,
        playVideo,
        cleanAllTransferCacheData
    }
}

const key = Symbol('TRANSFER_SERVICE')

export const useProvideTransfer = function () {
    const options = useTransfer()
    provideLocal(key, options)
    return options
}

export const useInjectTransfer = function () {
    return injectLocal(key)
}