/**
 * 文件功能：聊天室特效管理器
 *
 * 统一管理全屏 Canvas 特效的入口、防重入和资源清理。
 * 播放期间用户点击屏幕任意位置可立即结束当前全屏特效。
 * 使用方式：EffectManager.play('fireworks' | 'rain' | 'lightning' | 'snow' | 'sakura' | 'meteors' | 'gold-rain' | 'hearts' | 'confetti' | 'fireflies')
 */

const EffectManager = (() => {
    // 音效模块只在首次播放特效时加载，缓存 Promise 避免重复 import
    let _soundModulePromise = null;
    // 各特效模块加载器，使用静态路径让 Vite 拆分为可按需加载的 chunk
    const EFFECT_MODULES = {
        fireworks: { key: "fireworks", load: () => import("./fireworks.js") },
        "wedding-fireworks": { key: "fireworks", load: () => import("./fireworks.js") },
        rain: { key: "rain", load: () => import("./rain.js") },
        lightning: { key: "lightning", load: () => import("./lightning.js") },
        snow: { key: "snow", load: () => import("./snow.js") },
        sakura: { key: "sakura", load: () => import("./sakura.js") },
        meteors: { key: "meteors", load: () => import("./meteors.js") },
        "gold-rain": { key: "gold-rain", load: () => import("./gold-rain.js") },
        hearts: { key: "hearts", load: () => import("./hearts.js") },
        confetti: { key: "confetti", load: () => import("./confetti.js") },
        fireflies: { key: "fireflies", load: () => import("./fireflies.js") },
    };
    // 特效模块 Promise 缓存，同类型重复触发时复用同一次加载
    const _effectModulePromises = new Map();
    // 当前正在播放的特效名称（防止同时播放两个特效）
    let _current = null;
    // 当前特效返回的取消函数，用于手动停止时真正停掉内部 RAF / 定时器
    let _currentCancel = null;
    // 全屏 Canvas 元素引用
    let _canvas = null;
    // 待播放特效队列，避免多个进场效果互相打断
    const _queue = [];
    // 队列最多保留 3 个待播特效，避免高频触发后播放过期动画
    const MAX_QUEUE_LENGTH = 3;
    // 普通特效排队超过 10 秒后丢弃，避免播放过期动画
    const QUEUED_EFFECT_TTL = 10000;
    // 当前特效播放批次，用于忽略手动停止后的旧回调
    let _playToken = 0;
    // 是否已经绑定本轮点击停止监听
    let _clickStopBound = false;
    // 延迟绑定定时器，避免触发播放的同一次点击立即停止特效
    let _clickStopTimer = null;
    // 当前画布像素倍率固定为 1，避免高清手机放大绘制面积拖慢特效
    const MAX_DPR = 1;

    /**
     * 按窗口尺寸重置画布像素尺寸。
     *
     * @param {HTMLCanvasElement} canvas 全屏特效画布
     */
    function _resizeCanvas(canvas) {
        const ratio = Math.min(window.devicePixelRatio || 1, MAX_DPR);
        canvas.width = Math.floor(window.innerWidth * ratio);
        canvas.height = Math.floor(window.innerHeight * ratio);
    }

    /**
     * 获取或创建全屏 Canvas 元素
     * 属性：fixed 定位，覆盖全屏，播放期间接收点击用于立即停止特效。
     */
    function _getCanvas() {
        if (_canvas && document.body.contains(_canvas)) {
            return _canvas;
        }
        const c = document.createElement("canvas");
        c.id = "effect-canvas";
        c.style.cssText = [
            "position:fixed",
            "top:0",
            "left:0",
            "width:100vw",
            "height:100vh",
            "z-index:99999",
            "pointer-events:auto",
            "cursor:pointer",
            "touch-action:manipulation",
        ].join(";");
        _resizeCanvas(c);
        document.body.appendChild(c);
        _canvas = c;
        window.addEventListener("resize", _handleResize);
        window.addEventListener("orientationchange", _handleResize);
        return c;
    }

    /**
     * 响应窗口尺寸变化，确保手机横竖屏切换后覆盖范围正确。
     */
    function _handleResize() {
        if (_current) {
            stop();
            return;
        }

        if (_canvas) {
            _resizeCanvas(_canvas);
        }
    }

    /**
     * 绑定点击屏幕立即停止当前特效的监听。
     *
     * 延后一帧绑定，避免触发特效的同一次点击被误判为结束点击。
     */
    function _bindClickStop() {
        if (_clickStopBound) {
            return;
        }

        _clickStopTimer = window.setTimeout(() => {
            if (!_current || _clickStopBound) {
                return;
            }

            _clickStopBound = true;
            if (_canvas) {
                _canvas.addEventListener("pointerdown", _handleStopClick, {
                    capture: true,
                });
                _canvas.addEventListener("mousedown", _handleStopClick, {
                    capture: true,
                });
            }
            document.addEventListener("pointerdown", _handleStopClick, {
                capture: true,
            });
            document.addEventListener("mousedown", _handleStopClick, {
                capture: true,
            });
        }, 120);
    }

    /**
     * 解绑点击停止监听，避免特效结束后影响正常聊天操作。
     */
    function _unbindClickStop() {
        if (_clickStopTimer) {
            window.clearTimeout(_clickStopTimer);
            _clickStopTimer = null;
        }

        if (!_clickStopBound) {
            return;
        }

        if (_canvas) {
            _canvas.removeEventListener("pointerdown", _handleStopClick, {
                capture: true,
            });
            _canvas.removeEventListener("mousedown", _handleStopClick, {
                capture: true,
            });
        }
        document.removeEventListener("pointerdown", _handleStopClick, {
            capture: true,
        });
        document.removeEventListener("mousedown", _handleStopClick, {
            capture: true,
        });
        _clickStopBound = false;
    }

    /**
     * 处理屏幕点击结束特效。
     */
    function _handleStopClick(event) {
        if (event) {
            event.stopPropagation();
        }

        stop();
    }

    /**
     * 页面进入后台时停止特效，避免隐藏标签页继续运行 Canvas 和音效。
     */
    function _handleVisibilityChange() {
        if (document.hidden) {
            stop();
            _queue.length = 0;
        }
    }

    /**
     * 特效结束后清理 Canvas，重置状态，并停止音效。
     *
     * @param {Object} options 清理选项
     * @param {boolean} options.playNext 是否继续播放队列中的下一个特效
     * @param {number|null} options.token 当前特效播放批次
     */
    function _cleanup({ playNext = true, token = null, cancelCurrent = false } = {}) {
        if (token !== null && token !== _playToken) {
            return;
        }

        _playToken++;
        _unbindClickStop();

        if (cancelCurrent && typeof _currentCancel === "function") {
            _currentCancel();
        }
        _currentCancel = null;

        if (_canvas && document.body.contains(_canvas)) {
            document.body.removeChild(_canvas);
        }
        window.removeEventListener("resize", _handleResize);
        window.removeEventListener("orientationchange", _handleResize);
        _canvas = null;
        _current = null;
        // 通知音效引擎停止（兜底：正常情况下音效会自行计时结束）
        if (typeof window.EffectSounds !== "undefined") {
            window.EffectSounds.stop();
        }

        if (playNext) {
            const nextType = _dequeueNextType();
            if (nextType) {
                play(nextType);
            }
        }
    }

    /**
     * 将特效加入有限队列，同类型短时间重复触发时只保留一份。
     *
     * @param {string} type 待播放特效类型
     */
    function _enqueue(type) {
        const existingIndex = _queue.findIndex((item) => item.type === type);
        if (existingIndex !== -1) {
            _queue.splice(existingIndex, 1);
        }

        _queue.push({
            type,
            queuedAt: Date.now(),
            keepUntilPlayed: type === "wedding-fireworks",
        });
        while (_queue.length > MAX_QUEUE_LENGTH) {
            _queue.shift();
        }
    }

    /**
     * 取出下一个仍然有效的排队特效。
     *
     * @returns {string|null}
     */
    function _dequeueNextType() {
        const now = Date.now();

        while (_queue.length > 0) {
            const next = _queue.shift();
            if (next.keepUntilPlayed || now - next.queuedAt <= QUEUED_EFFECT_TTL) {
                return next.type;
            }
        }

        return null;
    }

    /**
     * 确保音效模块已经加载。
     *
     * @returns {Promise<void>}
     */
    function _loadSoundModule() {
        if (!_soundModulePromise) {
            _soundModulePromise = import("./effect-sounds.js");
        }

        return _soundModulePromise;
    }

    /**
     * 确保指定特效模块已经加载。
     *
     * @param {string} type 特效类型
     * @returns {Promise<void>}
     */
    function _loadEffectModule(type) {
        const moduleConfig = EFFECT_MODULES[type];
        if (!moduleConfig) {
            return Promise.reject(new Error(`未知特效类型：${type}`));
        }

        if (!_effectModulePromises.has(moduleConfig.key)) {
            _effectModulePromises.set(moduleConfig.key, moduleConfig.load());
        }

        return _effectModulePromises.get(moduleConfig.key);
    }

    /**
     * 播放前加载对应特效模块和音效模块。
     *
     * @param {string} type 特效类型
     * @returns {Promise<void>}
     */
    function _loadModulesFor(type) {
        return Promise.all([
            _loadEffectModule(type),
            _loadSoundModule(),
        ]).then(() => {});
    }

    /**
     * 记录具体特效返回的取消句柄。
     *
     * @param {Object|undefined} controller 特效启动返回值
     */
    function _bindEffectController(controller) {
        _currentCancel = typeof controller?.cancel === "function"
            ? controller.cancel
            : null;
    }

    /**
     * 启动具体特效并保存取消句柄。
     *
     * @param {Object|undefined} effectObject 特效全局对象
     * @param {HTMLCanvasElement} canvas 全屏特效画布
     * @param {Function} finishCurrent 当前特效结束回调
     * @param {string} startMethod 启动方法名称
     * @returns {boolean} 是否成功找到并启动特效
     */
    function _startEffect(effectObject, canvas, finishCurrent, startMethod = "start") {
        if (!effectObject || typeof effectObject[startMethod] !== "function") {
            return false;
        }

        _bindEffectController(effectObject[startMethod](canvas, finishCurrent));
        return true;
    }

    /**
     * 播放指定特效
     *
     * @param {string} type  特效类型：fireworks / rain / lightning / snow / sakura / meteors / gold-rain / hearts / confetti / fireflies
     */
    function play(type) {
        if (document.hidden) {
            return;
        }

        if (!EFFECT_MODULES[type]) {
            console.warn(`[EffectManager] 未知特效类型：${type}`);
            return;
        }

        // 防重入：同时只允许一个特效
        if (_current) {
            _enqueue(type);
            return;
        }

        _play(type);
    }

    /**
     * 加载模块后播放指定特效。
     *
     * @param {string} type 特效类型
     */
    async function _play(type) {
        _current = type;
        const token = _playToken;

        try {
            await _loadModulesFor(type);
        } catch (error) {
            console.error(`[EffectManager] 加载特效模块失败：${type}`, error);
            _cleanup({ token });
            return;
        }

        if (token !== _playToken || _current !== type) {
            return;
        }

        if (document.hidden) {
            _cleanup({ playNext: false, token });
            return;
        }

        const canvas = _getCanvas();
        const finishCurrent = () => _cleanup({ token });
        _bindClickStop();

        // 同步触发对应音效
        if (typeof window.EffectSounds !== "undefined") {
            window.EffectSounds.play(type);
        }

        let started = false;

        try {
            switch (type) {
                case "fireworks":
                    started = _startEffect(window.FireworksEffect, canvas, finishCurrent);
                    break;
                case "wedding-fireworks":
                    // 婚礼专属：双倍礼花，粉金浪漫配色，持续 12 秒
                    started = _startEffect(window.FireworksEffect, canvas, finishCurrent, "startDouble");
                    break;
                case "rain":
                    started = _startEffect(window.RainEffect, canvas, finishCurrent);
                    break;
                case "lightning":
                    started = _startEffect(window.LightningEffect, canvas, finishCurrent);
                    break;
                case "snow":
                    started = _startEffect(window.SnowEffect, canvas, finishCurrent);
                    break;
                case "sakura":
                    started = _startEffect(window.SakuraEffect, canvas, finishCurrent);
                    break;
                case "meteors":
                    started = _startEffect(window.MeteorsEffect, canvas, finishCurrent);
                    break;
                case "gold-rain":
                    started = _startEffect(window.GoldRainEffect, canvas, finishCurrent);
                    break;
                case "hearts":
                    started = _startEffect(window.HeartsEffect, canvas, finishCurrent);
                    break;
                case "confetti":
                    started = _startEffect(window.ConfettiEffect, canvas, finishCurrent);
                    break;
                case "fireflies":
                    started = _startEffect(window.FirefliesEffect, canvas, finishCurrent);
                    break;
                default:
                    console.warn(`[EffectManager] 未知特效类型：${type}`);
            }
        } catch (error) {
            console.error(`[EffectManager] 启动特效失败：${type}`, error);
        }

        if (!started) {
            finishCurrent();
        }
    }

    /**
     * 用户手动停止当前全屏特效。
     *
     * 会立即移除画布、停止音效，并清空排队中的后续特效。
     */
    function stop() {
        if (!_current) {
            return;
        }

        _queue.length = 0;
        _cleanup({ playNext: false, cancelCurrent: true });
    }

    document.addEventListener("visibilitychange", _handleVisibilityChange);

    return { play, stop };
})();

window.EffectManager = EffectManager;
