/**
 * 文件功能：聊天室金币雨特效
 *
 * 使用 Canvas 绘制翻转下落的金币，让金币带着高光与闪烁效果从天而降，
 * 用于活动奖励、红包雨等庆祝场景。
 */

const GoldRainEffect = (() => {
    class Coin {
        constructor(w, h) {
            this.w = w;
            this.h = h;
            this.reset(true);
        }

        /**
         * 重置金币位置与翻转参数。
         *
         * @param {boolean} initial 是否首次初始化
         */
        reset(initial = false) {
            this.x = Math.random() * this.w;
            this.y = initial ? Math.random() * this.h : -30 - Math.random() * 160;
            this.radius = Math.random() * 8 + 12;
            this.speedY = Math.random() * 1.2 + 1.2;
            this.speedX = Math.random() * 0.9 - 0.45;
            this.gravity = Math.random() * 0.035 + 0.015;
            this.spin = Math.random() * Math.PI * 2;
            this.spinSpeed = Math.random() * 0.16 + 0.1;
            this.alpha = Math.random() * 0.25 + 0.72;
            this.sparkle = Math.random() * Math.PI * 2;
        }

        /**
         * 更新金币状态。
         */
        update() {
            this.speedY = Math.min(this.speedY + this.gravity, 2.8);
            this.y += this.speedY;
            this.x += this.speedX;
            this.spin += this.spinSpeed;
            this.sparkle += 0.08;

            if (this.y > this.h + 40) {
                this.reset(false);
            }
        }

        /**
         * 绘制单枚金币。
         *
         * @param {CanvasRenderingContext2D} ctx
         */
        draw(ctx) {
            const scaleX = Math.max(0.22, Math.abs(Math.cos(this.spin)));
            const glow = 0.28 + Math.max(0, Math.sin(this.sparkle)) * 0.18;

            ctx.save();
            ctx.translate(this.x, this.y);
            ctx.scale(scaleX, 1);
            ctx.globalAlpha = this.alpha;
            ctx.shadowColor = "rgba(250, 204, 21, 0.55)";
            ctx.shadowBlur = 10;

            const gradient = ctx.createLinearGradient(0, -this.radius, 0, this.radius);
            gradient.addColorStop(0, "#fef08a");
            gradient.addColorStop(0.45, "#facc15");
            gradient.addColorStop(1, "#ca8a04");

            ctx.fillStyle = gradient;
            ctx.beginPath();
            ctx.arc(0, 0, this.radius, 0, Math.PI * 2);
            ctx.fill();

            ctx.lineWidth = 2;
            ctx.strokeStyle = "rgba(161, 98, 7, 0.7)";
            ctx.stroke();

            ctx.fillStyle = `rgba(255,255,255,${glow})`;
            ctx.beginPath();
            ctx.arc(-this.radius * 0.28, -this.radius * 0.32, this.radius * 0.26, 0, Math.PI * 2);
            ctx.fill();

            ctx.scale(1 / scaleX, 1);
            ctx.fillStyle = "rgba(120, 53, 15, 0.8)";
            ctx.font = `${Math.max(10, this.radius)}px Arial`;
            ctx.textAlign = "center";
            ctx.textBaseline = "middle";
            ctx.fillText("¥", 0, 1);
            ctx.restore();
        }
    }

    /**
     * 启动金币雨特效。
     *
     * @param {HTMLCanvasElement} canvas
     * @param {Function} onEnd
     */
    function start(canvas, onEnd) {
        const ctx = canvas.getContext("2d");
        const w = canvas.width;
        const h = canvas.height;
        const DURATION = 8600;
        const isMobile = window.matchMedia?.("(max-width: 640px)")?.matches || window.innerWidth <= 640;
        const densityScale = isMobile ? 0.72 : 1;
        const COIN_COUNT = Math.round(Math.min(58, Math.max(28, Math.floor(w / 28))) * densityScale);
        const coins = Array.from({ length: COIN_COUNT }, () => new Coin(w, h));
        const startTime = performance.now();
        let animId = null;
        let finished = false;

        /**
         * 统一结束金币雨动画，手动取消时只清理不回调。
         *
         * @param {boolean} canceled 是否为手动取消
         */
        function finish(canceled) {
            if (finished) {
                return;
            }

            finished = true;
            if (animId) {
                cancelAnimationFrame(animId);
            }
            ctx.clearRect(0, 0, w, h);
            if (!canceled) {
                onEnd();
            }
        }

        function animate(now) {
            ctx.clearRect(0, 0, w, h);

            coins.forEach((coin) => {
                coin.update();
                coin.draw(ctx);
            });

            if (now - startTime < DURATION) {
                animId = requestAnimationFrame(animate);
            } else {
                finish(false);
            }
        }

        animId = requestAnimationFrame(animate);
        return {
            cancel() {
                finish(true);
            },
        };
    }

    return { start };
})();

window.GoldRainEffect = GoldRainEffect;
