/**
 * 文件功能：聊天室下雪特效
 *
 * 使用 Canvas 同时绘制远景小雪与近景六角雪花，
 * 通过层次、大小、速度差营造更饱满的飘雪效果。
 */

const SnowEffect = (() => {
    /**
     * 在指定位置绘制一朵六角雪花（深色轮廓 + 白色主体）
     *
     * @param  {CanvasRenderingContext2D}  ctx
     * @param  {number}  x      中心 x
     * @param  {number}  y      中心 y
     * @param  {number}  r      主臂长度
     * @param  {number}  alpha  透明度
     * @param  {number}  rot    旋转角度（弧度）
     */
    function _drawFlake(ctx, x, y, r, alpha, rot) {
        ctx.save();
        ctx.globalAlpha = alpha;
        ctx.lineCap = "round";
        ctx.translate(x, y);
        ctx.rotate(rot);

        // 两遍绘制：先深蓝色粗描边，再白色细线覆盖
        // 这样在浅蓝、白色等背景上都清晰可辨
        const passes = [
            { color: "rgba(30, 60, 140, 0.8)", lw: r * 0.22 + 2.5 }, // 深蓝粗描边
            { color: "rgba(255, 255, 255, 1.0)", lw: Math.max(1, r * 0.11) }, // 白色主体
        ];

        passes.forEach(({ color, lw }) => {
            ctx.strokeStyle = color;
            ctx.lineWidth = lw;

            for (let i = 0; i < 6; i++) {
                ctx.save();
                ctx.rotate((Math.PI / 3) * i);

                // 主臂
                ctx.beginPath();
                ctx.moveTo(0, 0);
                ctx.lineTo(r, 0);
                ctx.stroke();

                // 斜向分叉（0.4r 和 0.65r 处各一对）
                const branchLen = r * 0.35;
                const branchAngle = Math.PI / 4; // 45°

                [0.4, 0.65].forEach((pos) => {
                    const bx = r * pos;
                    // 上分叉
                    ctx.beginPath();
                    ctx.moveTo(bx, 0);
                    ctx.lineTo(
                        bx + Math.cos(branchAngle) * branchLen,
                        Math.sin(branchAngle) * branchLen,
                    );
                    ctx.stroke();
                    // 下分叉
                    ctx.beginPath();
                    ctx.moveTo(bx, 0);
                    ctx.lineTo(
                        bx + Math.cos(branchAngle) * branchLen,
                        -Math.sin(branchAngle) * branchLen,
                    );
                    ctx.stroke();
                });

                ctx.restore();
            }
        });

        ctx.restore();
    }

    /**
     * 绘制远景小雪点，让画面更密实，不会只看到零散大雪花。
     *
     * @param  {CanvasRenderingContext2D}  ctx
     * @param  {number}  x
     * @param  {number}  y
     * @param  {number}  radius
     * @param  {number}  alpha
     */
    function _drawSoftSnow(ctx, x, y, radius, alpha) {
        ctx.save();
        ctx.globalAlpha = alpha;
        ctx.fillStyle = "rgba(255,255,255,0.95)";
        ctx.shadowColor = "rgba(255,255,255,0.8)";
        ctx.shadowBlur = 8;
        ctx.beginPath();
        ctx.arc(x, y, radius, 0, Math.PI * 2);
        ctx.fill();
        ctx.restore();
    }

    // 雪花粒子类
    class Flake {
        constructor(w, h, layer = "front") {
            this.w = w;
            this.h = h;
            this.layer = layer;
            this.reset(true);
        }

        /**
         * 重置雪花，让前景和背景使用不同参数。
         *
         * @param {boolean} initial
         */
        reset(initial = false) {
            this.x = Math.random() * this.w;
            this.y = initial ? Math.random() * this.h : -20;
            if (this.layer === "back") {
                this.r = Math.random() * 2 + 1.1;
                this.speed = Math.random() * 0.55 + 0.28;
                this.drift = Math.random() * 0.28 - 0.14;
                this.alpha = Math.random() * 0.25 + 0.28;
            } else {
                this.r = Math.random() * 9 + 6.5;
                this.speed = Math.random() * 1.05 + 0.48;
                this.drift = Math.random() * 0.7 - 0.35;
                this.alpha = Math.random() * 0.25 + 0.68;
            }
            this.rot = Math.random() * Math.PI * 2;
            this.rotSpd = (Math.random() - 0.5) * (this.layer === "back" ? 0.008 : 0.018);
            this.wobble = 0;
            this.wobSpd = Math.random() * (this.layer === "back" ? 0.02 : 0.028) + 0.008;
        }

        update() {
            this.wobble += this.wobSpd;
            this.x += Math.sin(this.wobble) * (this.layer === "back" ? 0.28 : 0.58) + this.drift;
            this.y += this.speed;
            this.rot += this.rotSpd;
            if (this.y > this.h + 20) {
                this.reset(false);
            }
        }

        draw(ctx) {
            if (this.layer === "back") {
                _drawSoftSnow(ctx, this.x, this.y, this.r, this.alpha);
                return;
            }

            _drawFlake(ctx, this.x, this.y, this.r, this.alpha, this.rot);
        }
    }

    /**
     * 启动下雪特效
     *
     * @param  {HTMLCanvasElement}  canvas   全屏 Canvas
     * @param  {Function}           onEnd    特效结束回调
     */
    function start(canvas, onEnd) {
        const ctx = canvas.getContext("2d");
        const w = canvas.width;
        const h = canvas.height;
        const DURATION = 10000;
        const isMobile = window.matchMedia?.("(max-width: 640px)")?.matches || window.innerWidth <= 640;
        const densityScale = isMobile ? 0.68 : 1;
        const backFlakeCount = Math.round(Math.min(120, Math.max(70, Math.floor(w / 18))) * densityScale);
        const frontFlakeCount = Math.round(Math.min(64, Math.max(34, Math.floor(w / 42))) * densityScale);
        const flakes = [
            ...Array.from(
                { length: backFlakeCount },
                () => new Flake(w, h, "back"),
            ),
            ...Array.from(
                { length: frontFlakeCount },
                () => new Flake(w, h, "front"),
            ),
        ];

        const breezeBands = Array.from({ length: isMobile ? 1 : 2 }, () => ({
            x: Math.random() * w,
            y: Math.random() * h,
            radius: 180 + Math.random() * 140,
            alpha: Math.random() * 0.05 + 0.025,
            drift: Math.random() * 0.3 + 0.08,
        }));

        let animId = null;
        let finished = false;
        const startTime = performance.now();

        /**
         * 统一结束雪花动画，手动取消时只清理不回调。
         *
         * @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);

            // 加一层极淡的冷白雾感，让雪景更有氛围但不遮挡聊天内容。
            const mist = ctx.createLinearGradient(0, 0, 0, h);
            mist.addColorStop(0, "rgba(226,240,255,0.08)");
            mist.addColorStop(0.4, "rgba(226,240,255,0.03)");
            mist.addColorStop(1, "rgba(226,240,255,0)");
            ctx.fillStyle = mist;
            ctx.fillRect(0, 0, w, h);

            breezeBands.forEach((band) => {
                band.x += band.drift;
                if (band.x - band.radius > w) {
                    band.x = -band.radius;
                    band.y = Math.random() * h;
                }

                const breeze = ctx.createRadialGradient(
                    band.x,
                    band.y,
                    0,
                    band.x,
                    band.y,
                    band.radius,
                );
                breeze.addColorStop(0, `rgba(255,255,255,${band.alpha})`);
                breeze.addColorStop(1, "rgba(255,255,255,0)");
                ctx.fillStyle = breeze;
                ctx.beginPath();
                ctx.arc(band.x, band.y, band.radius, 0, Math.PI * 2);
                ctx.fill();
            });

            flakes.forEach((f) => {
                f.update();
                f.draw(ctx);
            });

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

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

    return { start };
})();

window.SnowEffect = SnowEffect;
