/** 
 * Uniapp 事件总线类，用于在应用内进行事件通信
 * 支持同步和异步事件触发，可配置是否继续传播事件
 * 可配置是否在任意监听器抛出错误后立即停止传播
 * 可配置是否在每个监听器执行后调用 defaultAsyncHandler
 * 如果 allowAsyncHandlerRun 为 true，在每个监听器执行后调用 defaultAsyncHandler
 * 如果 allowAsyncHandlerRun 为 false，不调用 defaultAsyncHandler
 * 注意：如果 allowAsyncHandlerRun 为 false，defaultAsyncHandler 不会被调用，也不会等待其 resolve
 * 
 * @author ZFSun <lauer3912@gmail.com>
 * @version 1.0.0
 * @createTime 2023-08-10
 */

class EventBus {
    constructor() {
        this.events = new Map()
        this.domBridge = null
        this.platform = this.detectPlatform()
    }

    // 检测平台
    detectPlatform() {
        // #ifdef H5
        return 'h5'
        // #endif

        // #ifdef MP-WEIXIN
        return 'weapp'
        // #endif

        // #ifdef MP-ALIPAY
        return 'alipay'
        // #endif

        // #ifdef MP-BAIDU
        return 'baidu'
        // #endif

        // #ifdef MP-TOUTIAO
        return 'toutiao'
        // #endif

        // #ifdef MP-QQ
        return 'qq'
        // #endif

        // #ifdef MP-KUAISHOU
        return 'kuaishou'
        // #endif

        // #ifdef MP-JD
        return 'jd'
        // #endif

        // #ifdef MP-360
        return '360'
        // #endif

        // #ifdef MP-XHS
        return 'xhs'
        // #endif


        return 'unknown'
    }

    // 设置 DOM 桥接
    setDomBridge(bridge) {
        this.domBridge = bridge
        bridge.setEventBus(this)
    }

    // 监听事件（自动处理平台差异），支持取消
    on(eventName, callback, options = { priority: 0 }) {
        // 如果是 H5 平台且事件名是 DOM 事件，自动创建桥接
        if (this.platform === 'h5' && this.isDomEvent(eventName) && this.domBridge) {
            return this.domBridge.bridgeDomEvent(
                this.getDomEventName(eventName),
                eventName,
                options
            )
        }

        // 普通 EventBus 监听
        if (!this.events.has(eventName)) {
            this.events.set(eventName, new Set())
        }

        const handler = { callback, options }
        this.events.get(eventName).add(handler)

        // 返回取消函数
        return () => this.off(eventName, handler)
    }

    // 监听多个事件，支持取消
    onMany(eventNames = [], callback, options = { priority: 0 }) {
        // 支持字符串数组或空格分隔的字符串，自动转换为数组
        if (typeof eventNames === 'string') {
            // 支持使用空格分隔事件名
            // 支持使用逗号分隔事件名
            // 下面代码使用正则表达式，自动处理逗号和空格分隔的字符串
            eventNames = eventNames.replace(/\s+/g, ',').split(',')
        } 
        const removeFns = eventNames.map(eventName => this.on(eventName, callback, options))
        return () => removeFns.forEach(fn => fn())
    }


    // 异步触发事件，支持取消传播，并支持在监听处理后调用异步函数 defaultAsyncHandler
    // 使用方式：await eventBus.emit(eventName, data, defaultAsyncHandler)
    // defaultAsyncHandler 会在监听器执行后被调用，签名为 defaultAsyncHandler(event, handler, handlerResult)
    // 如果 defaultAsyncHandler 返回 Promise，会等待其 resolve
    // 如果 allowContinuePropagation 为 false，事件触发后将立即返回，不继续传播
    // 如果 stopOnError 为 true，在任意监听器抛出错误后立即停止传播
    // 如果 allowAsyncHandlerRun 为 true，在每个监听器执行后调用 defaultAsyncHandler
    // 如果 allowAsyncHandlerRun 为 false，不调用 defaultAsyncHandler
    // 注意：如果 allowAsyncHandlerRun 为 false，defaultAsyncHandler 不会被调用，也不会等待其 resolve
    async emit(eventName, data, defaultAsyncHandler, { allowContinuePropagation = true, stopOnError = false, allowAsyncHandlerRun = true } = {}) {
        // 先创建事件对象，方便传入 defaultAsyncHandler
        const event = this.createEvent(eventName, data)

        // helper to call defaultAsyncHandler only when allowed
        const callDefaultAsyncHandler = async (evt, handler, handlerResult) => {
            // console.log('callDefaultAsyncHandler', {evt, handler, handlerResult});
            if (typeof defaultAsyncHandler !== 'function' || !allowAsyncHandlerRun) return
            try {
                const res = defaultAsyncHandler(evt, handler, handlerResult)
                if (res instanceof Promise) {
                    const awaited = await res
                    if (awaited === false) evt.preventDefault()
                } else {
                    if (res === false) evt.preventDefault()
                }
            } catch (err) {
                console.error(`EventBus defaultAsyncHandler error for ${eventName}:`, err)
            }
        }

        // 如果是 H5 平台且需要触发 DOM 事件
        if (this.platform === 'h5' && data && data.triggerDomEvent) {
            const domResult = this.domBridge.triggerDomEventFromEventBus(
                this.getDomEventName(eventName),
                data.detail
            )

            // 调用 defaultAsyncHandler（仅当允许）
            await callDefaultAsyncHandler(event, null, domResult)

            // 如果不允许继续传播，则直接返回（根据 defaultPrevented 判断结果）
            if (!allowContinuePropagation) {
                return !event.defaultPrevented
            }
            // 否则继续走普通 EventBus 处理（fallthrough）
        } 

        // 普通 EventBus 触发
        const handlers = this.events.get(eventName)

        // 如果没有监听器，执行 defaultAsyncHandler（如果有），并返回结果
        if (!handlers) {
            await callDefaultAsyncHandler(event, null, true)
            return !event.defaultPrevented
        }

        // 如果不允许继续传播，则不执行任何监听器，只调用一次 defaultAsyncHandler（若提供），然后返回
        if (!allowContinuePropagation) {
            await callDefaultAsyncHandler(event, null, true)
            return !event.defaultPrevented
        }

        // 按优先级排序执行
        const sortedHandlers = Array.from(handlers).sort((a, b) => {
            return (b.options?.priority || 0) - (a.options?.priority || 0)
        })

        // 按优先级顺序执行每个监听器
        for (const handler of sortedHandlers) {
            if (event.defaultPrevented) break

            try {
                // 支持监听器返回 Promise 或同步值
                const result = handler.callback(event)
                const awaitedResult = result instanceof Promise ? await result : result

                // 如果回调返回 false，也停止默认行为
                if (awaitedResult === false) {
                    event.preventDefault()
                }

                // 在每个监听器执行后，如果允许并且提供了 defaultAsyncHandler，就调用并等待它
                // console.log('==> callDefaultAsyncHandler', {event, handler, awaitedResult})
                await callDefaultAsyncHandler(event, handler, awaitedResult)
            } catch (error) {
                console.error(`EventBus ${eventName} error:`, error)
                // 如果全局或该 handler 指定遇错停止传播，则停止
                if (stopOnError || handler.options?.stopOnError) {
                    break
                }
                // 否则继续下一个 handler
            }

            // 一次性监听自动移除
            if (handler.options?.once) {
                this.off(eventName, handler)
            }
        }

        return !event.defaultPrevented
    }


    // 移除监听
    off(eventName, handler) {
        const handlers = this.events.get(eventName)
        if (handlers) {
            handlers.delete(handler)
            if (handlers.size === 0) {
                this.events.delete(eventName)
            }
        }
    }

    // 移除多个事件监听
    offMany(eventNames, handler) {
        eventNames.forEach(eventName => this.off(eventName, handler))
    }

    // 移除所有监听
    offAll(eventName) {
        this.events.delete(eventName)
    }

    // 一次性监听
    once(eventName, callback, options = {}) {
        return this.on(eventName, callback, { ...options, once: true })
    }

    // 前置监听（高优先级）
    preOn(eventName, callback) {
        return this.on(eventName, callback, { priority: 100 })
    }

    // 后置监听（低优先级）
    postOn(eventName, callback) {
        return this.on(eventName, callback, { priority: -100 })
    }

    // 判断是否是 DOM 事件
    isDomEvent(eventName) {
        const domEvents = ['click', 'touchstart', 'touchend', 'mousedown', 'mouseup']
        return domEvents.includes(eventName.toLowerCase())
    }

    // 获取对应的 DOM 事件名
    getDomEventName(eventName) {
        const map = {
            'tap': 'click',
            'click': 'click',
            'touchstart': 'touchstart',
            'touchend': 'touchend'
        }
        return map[eventName] || eventName
    }

    // 创建可取消的事件对象
    createEvent(eventName, data) {
        const event = {
            type: eventName,
            data,
            timestamp: Date.now(),
            defaultPrevented: false,
            _isPropagationStopped: false
        }

        // 阻止事件继续传播
        event.preventDefault = function () {
            this.defaultPrevented = true
        }

        // 停止传播
        event.stopPropagation = function () {
            this._isPropagationStopped = true
        }

        event.isPropagationStopped = function () {
            return this._isPropagationStopped
        }

        return event
    }
}

export default new EventBus()