{{--
    文件功能：赛马竞猜前台弹窗组件

    聊天室内赛马竞猜游戏面板：
    - 监听 WebSocket horse.opened 事件触发弹窗
    - 展示参赛马匹列表和实时注池赔率
    - 倒计时押注后进入跑马阶段（动态进度条）
    - 监听 horse.progress 更新赛道动画
    - 监听 horse.settled 展示结果 + 个人赔付
    - 展示近10场历史趋势
--}}

{{-- ─── 赛马主面板 ─── --}}
<div id="horse-race-panel" x-data="horseRacePanel()" x-show="show" x-cloak>
    <div x-transition:enter="transition ease-out duration-300" x-transition:enter-start="opacity-0 scale-95"
        x-transition:enter-end="opacity-100 scale-100" x-transition:leave="transition ease-in duration-200"
        x-transition:leave-start="opacity-100 scale-100" x-transition:leave-end="opacity-0 scale-95"
        @click.self="close()"
        style="position:fixed; inset:0; background:rgba(0,0,0,.55); z-index:9941;
                display:flex; align-items:center; justify-content:center;">

        <div
            style="width:500px; max-width:96vw; border-radius:8px; overflow:hidden;
                    box-shadow:0 8px 32px rgba(0,0,0,.3); font-family:'Microsoft YaHei',SimSun,sans-serif; background:#fff;">

            {{-- ─── 标题栏（海军蓝风格）─── --}}
            <div
                style="background:linear-gradient(135deg,#336699,#5a8fc0); padding:10px 16px; display:flex; align-items:center; gap:10px;">
                <div style="font-size:14px; font-weight:bold; color:#fff; flex:1;">
                    🐎 赛马竞猜
                    <span style="font-size:11px; font-weight:normal; color:rgba(255,255,255,.6); margin-left:4px;"
                        x-text="raceId ? '#' + raceId + ' 场' : ''"></span>
                </div>
                {{-- 金币余额 --}}
                <div
                    style="font-size:12px; color:#d0e8ff; display:flex; align-items:center; gap:3px;
                            background:rgba(0,0,0,.2); padding:2px 8px; border-radius:10px;">
                    💰 <strong style="color:#ffe082; font-size:13px;" x-text="Number(window.chatContext?.userJjb || 0).toLocaleString()">--</strong> 金币
                </div>
                {{-- 倒计时（押注阶段） --}}
                <div x-show="phase === 'betting'"
                    style="font-size:12px; color:#fff; background:rgba(255,255,255,.2); padding:4px 12px; border-radius:20px; border:1px solid rgba(255,255,255,.3); box-shadow:0 2px 4px rgba(0,0,0,.1) inset; display:flex; align-items:center; gap:4px;">
                    ⏳ 剩 <span x-text="countdown" style="font-weight:bold; color:#fef08a; font-size:14px;"></span> 秒
                </div>
                {{-- 跑马阶段 --}}
                <div x-show="phase === 'running'" style="display:none;"
                    style="font-size:12px; color:rgba(255,255,255,.8); background:rgba(0,0,0,.2); padding:2px 10px; border-radius:10px;">
                    🏇 跑马中…
                </div>
                {{-- 结算 --}}
                <div x-show="phase === 'settled'" style="display:none;"
                    style="font-size:12px; color:#ffe082; font-weight:bold;">🏆 已结算</div>
                <span onclick="Alpine.$data(document.getElementById('horse-race-panel')).close()"
                    style="cursor:pointer; font-size:18px; color:rgba(255,255,255,.8); line-height:1; transition:opacity .15s;"
                    onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=.8">&times;</span>
            </div>

            {{-- 押注进度条（蓝色风格） --}}
            <div x-show="phase === 'betting'" style="height:3px; background:#d0e4f5; overflow:hidden;">
                <div style="height:100%; background:#336699; transition:width 1s linear;"
                    :style="'width:' + Math.max(0, (countdown / totalSeconds * 100)) + '%'"></div>
            </div>

            {{-- ─── 历史趋势（蓝白色系）─── --}}
            <div
                style="background:#f6faff; padding:6px 16px; border-bottom:1px solid #d0e4f5;
                        display:flex; gap:5px; align-items:center; flex-wrap:wrap; min-height:32px;">
                <span style="color:#336699; font-size:11px; margin-right:2px; font-weight:bold;">近期冒涨：</span>
                <template x-for="h in history" :key="h.id">
                    <span
                        style="padding:1px 8px; border-radius:10px; font-size:10px; font-weight:bold;
                                 background:#e8f0f8; color:#336699; border:1px solid #b8d0e8;"
                        :title="'#' + h.id + ' 冠军：' + h.winner_name" x-text="h.winner_name"></span>
                </template>
                <span x-show="history.length === 0" style="color:#aaa; font-size:11px;">暂无记录</span>
            </div>

            {{-- ─── 主体内容（白底）─── --}}
            <div style="background:#fff; padding:14px 16px;">

                {{-- ── 押注阶段 ── --}}
                <div x-show="phase === 'betting'">
                    {{-- 注池统计 --}}
                    <div
                        style="color:#336699; font-size:11px; margin-bottom:8px; text-align:center; background:#e8f0f8; border-radius:4px; padding:4px 0;">
                        注池总额：<span style="color:#b45309; font-weight:bold;"
                            x-text="Number(totalPool).toLocaleString() + ' 金币'"></span>
                    </div>

                    {{-- 马匹列表 --}}
                    <div style="display:flex; flex-direction:column; gap:8px; margin-bottom:12px;">
                        <template x-for="horse in horses" :key="horse.id">
                            <div style="border-radius:12px; padding:10px 14px; cursor:pointer; transition:all .15s; border:2px solid transparent;"
                                :style="myBet && myBetHorseId === horse.id ?
                                    'background:linear-gradient(135deg,#dff7e8,#f5fff8); border-color:#22c55e; box-shadow:0 0 0 3px rgba(34,197,94,.15), 0 10px 24px rgba(34,197,94,.10);' :
                                    (selectedHorse === horse.id ?
                                        'background:linear-gradient(135deg,#e8f0f8,#f7fbff); border-color:#336699; box-shadow:0 0 0 3px rgba(51,102,153,.12), 0 10px 24px rgba(51,102,153,.10); transform:translateY(-1px);' :
                                        'background:#f6faff; border-color:#d0e4f5;')"
                                @click="myBet ? null : selectedHorse = horse.id">
                                <div style="display:flex; align-items:center; justify-content:space-between;">
                                    <div style="display:flex; align-items:center; gap:10px;">
                                        {{-- 选中勾选 --}}
                                        <div style="width:24px; height:24px; border-radius:999px; border:2px solid; display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:bold; flex-shrink:0; transition:all .15s;"
                                            :style="myBet && myBetHorseId === horse.id ?
                                                'border-color:#16a34a; background:#16a34a; color:#fff; box-shadow:0 0 0 4px rgba(34,197,94,.16);' :
                                                (selectedHorse === horse.id ?
                                                    'border-color:#336699; background:#336699; color:#fff; box-shadow:0 0 0 4px rgba(51,102,153,.14);' :
                                                    'border-color:#b0c8e0; color:transparent; background:#fff;')">
                                            <span x-text="myBet && myBetHorseId === horse.id ? '押' : '✓'"></span>
                                        </div>
                                        <div style="font-size:22px;" x-text="horse.emoji"></div>
                                        <div>
                                            <div style="color:#225588; font-weight:bold; font-size:13px; display:flex; align-items:center; gap:6px;"
                                                x-text="horse.name"></div>
                                            <div x-show="myBet && myBetHorseId === horse.id"
                                                style="display:none; margin-top:2px; color:#15803d; font-size:10px; font-weight:bold;">
                                                我的押注
                                            </div>
                                            <div style="color:#888; font-size:10px;">
                                                注池：<span x-text="Number(horse.pool || 0).toLocaleString()"></span>
                                            </div>
                                        </div>
                                    </div>
                                    {{-- 实时赔率 --}}
                                    <div style="text-align:right;">
                                        <div style="color:#b45309; font-weight:900; font-size:15px;"
                                            x-text="horse.odds ? '×' + horse.odds : '—'"></div>
                                        <div style="color:#999; font-size:10px;">赔率</div>
                                    </div>
                                </div>
                            </div>
                        </template>
                    </div>

                    {{-- 已下注状态 --}}
                    <div x-show="myBet">
                        <div
                            style="background:linear-gradient(135deg,#e7fbe8,#f6fff7); border:2px solid #86efac; border-radius:14px;
                                    padding:14px 16px; text-align:center; margin-bottom:10px; box-shadow:0 10px 24px rgba(34,197,94,.10);">
                            <div style="color:#16a34a; font-weight:900; font-size:18px; letter-spacing:.02em;">
                                ✅ 已押注「<span x-text="myBetHorseName"></span>」
                                <span x-text="Number(myBetAmount).toLocaleString()"></span> 金币
                            </div>
                            <div style="color:#5f7a68; font-size:13px; margin-top:8px; font-weight:bold;">等待开跑…</div>
                        </div>
                    </div>

                    {{-- 下注区 --}}
                    <div x-show="!myBet">
                        {{-- 快捷金额 --}}
                        <div style="display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:10px; margin-bottom:14px;">
                            <template x-for="preset in quickBetAmounts" :key="preset">
                                <button @click="betAmount = preset"
                                    style="position:relative; overflow:hidden; border:1px solid #d6e5f6; border-radius:16px; padding:12px 0;
                                           font-size:16px; font-weight:900; letter-spacing:.01em; cursor:pointer; transition:all .18s ease; text-shadow:0 1px 0 rgba(255,255,255,.45);"
                                    :style="betAmount === preset ?
                                        'background:linear-gradient(180deg,#4f89c0 0%,#2d6297 52%,#214f7a 100%); color:#fff; border-color:#1c486f; box-shadow:0 12px 20px rgba(35,89,138,.30), inset 0 1px 0 rgba(255,255,255,.18); transform:translateY(-2px) scale(1.01); text-shadow:none;' :
                                        'background:linear-gradient(180deg,#ffffff 0%,#f3f8ff 65%,#e8f1fb 100%); color:#2f6498; box-shadow:0 6px 12px rgba(76,122,172,.10), inset 0 1px 0 rgba(255,255,255,.92);'"
                                    x-text="preset >= 10000 ? (preset/1000)+'k' : (preset >= 1000 ? (preset/1000)+'k' : preset)"></button>
                            </template>
                        </div>
                        <input type="number" x-model.number="betAmount" min="100" placeholder="自定义金额"
                            style="width:100%; background:#f6faff; border:1px solid #d0e4f5; border-radius:16px;
                                   padding:14px 16px; color:#23364d; font-size:16px; font-weight:bold; box-sizing:border-box; margin-bottom:14px; outline:none; transition:all .15s;"
                            onfocus="this.style.borderColor='#336699'; this.style.background='#fff'; this.style.boxShadow='0 0 0 2px rgba(51,102,153,.1)';"
                            onblur="this.style.borderColor='#d0e4f5'; this.style.background='#f6faff'; this.style.boxShadow='none';">

                        {{-- 下注按钮 --}}
                        <button @click="submitBet()" :disabled="!selectedHorse || betAmount < 100 || submitting"
                            :style="(!selectedHorse || betAmount < 100 || submitting) ?
                            'display:block; width:100%; border:none; border-radius:12px; padding:13px 0; font-size:14px; font-weight:bold; cursor:not-allowed; transition:all .2s; background:#e0e8f0; color:#99a8b8; box-shadow:none; font-family:inherit;' :
                            'display:block; width:100%; border:none; border-radius:12px; padding:13px 0; font-size:14px; font-weight:bold; cursor:pointer; transition:all .2s; background:linear-gradient(135deg,#336699,#5a8fc0); color:#fff; box-shadow:0 4px 14px rgba(51,102,153,.3); font-family:inherit;'">
                            <span
                                x-text="submitting ? '提交中…' : (!selectedHorse ? '请先选择马匹' : '🐎 押注「' + myBetHorsePreviewName + '」 ' + Number(betAmount).toLocaleString() + ' 金币')"></span>
                        </button>
                    </div>
                </div>

                {{-- ── 未开始阶段 ── --}}
                <div x-show="phase === 'idle'" style="display:none;">
                    <div style="padding:16px 4px 10px;">
                        <div style="background:linear-gradient(135deg,#f7fbff,#eef6ff); border:1px solid #c9def2; border-radius:16px; padding:22px 18px; text-align:center; box-shadow:inset 0 1px 0 rgba(255,255,255,.9);">
                            <div style="font-size:34px; margin-bottom:8px;">🐎</div>
                            <div style="color:#24507a; font-size:18px; font-weight:900; margin-bottom:6px;">游戏还没开始</div>
                            <div style="color:#6b7f95; font-size:13px; line-height:1.7;">
                                当前暂无进行中的赛马场次。<br>
                                请等待系统开场后再来下注。
                            </div>
                        </div>
                    </div>
                </div>

                {{-- ── 跑马阶段 ── --}}
                <div x-show="phase === 'running'" style="display:none;">
                    <div
                        style="margin-bottom:8px; color:#336699; font-size:11px; font-weight:bold; text-align:center; background:#e8f0f8; border-radius:4px; padding:4px;">
                        🏁 赛道实况
                    </div>
                    <div style="display:flex; flex-direction:column; gap:8px;">
                        <template x-for="horse in horses" :key="horse.id">
                            <div style="display:flex; align-items:center; gap:8px;">
                                <div style="width:30px; text-align:center; font-size:18px;" x-text="horse.emoji"></div>
                                <div style="flex:1; position:relative;">
                                    {{-- 赛道背景 --}}
                                    <div
                                        style="height:24px; background:#e8f0f8; border-radius:10px; overflow:hidden; position:relative;">
                                        {{-- 进度条 --}}
                                        <div style="height:100%; border-radius:20px; transition:width .9s ease-out;"
                                            :style="'width:' + (positions[horse.id] || 0) + '%; background:' +
                                            (leaderId === horse.id ? '#336699' :
                                                '#b8d0e8')">
                                        </div>
                                        {{-- 马匹图标（跟随进度） --}}
                                        <div style="position:absolute; top:0; bottom:0; display:flex; align-items:center; font-size:18px; line-height:1; transition:left .9s ease-out, transform .25s ease-out; pointer-events:none; will-change:left,transform; z-index:1;"
                                            :style="{
                                                left: (positions[horse.id] || 0) + '%',
                                                transform: 'translateX(-100%) ' + (leaderId === horse.id ? 'scale(1.2)' : 'scale(1)'),
                                                animation: (positions[horse.id] || 0) > 0 ? 'horse-run .45s ease-in-out infinite alternate' : 'none'
                                            }">
                                            <span x-text="horse.emoji"></span>
                                        </div>
                                    </div>
                                </div>
                                {{-- 进度数字 --}}
                                <div style="width:38px; text-align:right; color:#336699; font-size:10px; font-weight:bold;"
                                    x-text="(positions[horse.id] || 0) + '%'"></div>
                            </div>
                        </template>
                    </div>
                    <div style="margin-top:10px; text-align:center; color:#aaa; font-size:10px; text-align:center;">
                        🏁 终点线
                    </div>
                </div>

                {{-- ── 结算阶段（蓝白风格）── --}}
                <div x-show="phase === 'settled'" style="display:none;">
                    {{-- 获胜马匹 --}}
                    <div
                        style="text-align:center; padding:12px 0 10px; border-bottom:1px solid #d0e4f5; margin-bottom:10px;">
                        <div style="font-size:36px; margin-bottom:4px;" x-text="winnerEmoji"></div>
                        <div style="color:#336699; font-size:17px; font-weight:bold;"
                            x-text="'🏆 ' + winnerName + ' 夺冠！'">
                        </div>
                        <div style="color:#888; font-size:11px; margin-top:3px;"
                            x-text="'注池总额：' + Number(totalPool).toLocaleString() + ' 金币'"></div>
                    </div>

                    {{-- 个人结果 --}}
                    <div x-show="myBet">
                        {{-- 中奖 --}}
                        <div x-show="myWon"
                            style="border-radius:6px; padding:12px 14px; text-align:center; margin-bottom:4px;
                                    background:#e8fde8; border:1px solid #a3e6b0;">
                            <div style="font-size:24px; margin-bottom:4px;">🎉</div>
                            <div style="color:#16a34a; font-size:16px; font-weight:bold;">恭喜中奖！</div>
                            <div style="color:#15803d; font-size:18px; font-weight:bold; margin:4px 0;"
                                x-text="'+' + Number(myPayout).toLocaleString() + ' 💰'"></div>
                            <div style="color:#888; font-size:10px;"
                                x-text="'押「' + myBetHorseName + '」' + Number(myBetAmount).toLocaleString() + ' 金币 → 赢得 ' + Number(myPayout).toLocaleString() + ' 金币'">
                            </div>
                        </div>
                        {{-- 未中奖 --}}
                        <div x-show="!myWon"
                            style="border-radius:6px; padding:12px 14px; text-align:center;
                                    background:#fff0f0; border:1px solid #fca5a5;">
                            <div style="font-size:20px; margin-bottom:4px;">😔</div>
                            <div style="color:#dc2626; font-size:13px; font-weight:bold; margin-bottom:4px;">本场未中奖
                            </div>
                            <div style="color:#888; font-size:11px;"
                                x-text="'押了「' + myBetHorseName + '」' + Number(myBetAmount).toLocaleString() + ' 金币，冠军是「' + winnerName + '」'">
                            </div>
                        </div>
                    </div>
                    <div x-show="!myBet" style="text-align:center; color:#aaa; font-size:12px; padding:8px 0;">
                        本场未参与下注
                    </div>
                </div>
            </div>

            {{-- ─── 底部关闭 ─── --}}
            <div
                style="background:#fff; border-top:1px solid #d0e4f5; padding:14px 16px; display:flex; flex-direction:column; align-items:center; gap:8px;">
                <button @click="close()"
                    style="padding:10px 48px; min-width:140px; background:#f0f6ff; border:1px solid #b0d0ee; border-radius:12px;
                           font-size:14px; font-weight:bold; color:#336699; cursor:pointer; transition:all .15s;"
                    onmouseover="this.style.background='#ddeeff'" onmouseout="this.style.background='#f0f6ff'">
                    <span x-show="phase !== 'settled'">关闭</span>
                    <span x-show="phase === 'settled'" x-text="'关闭 (' + settledCountdown + 's)'"></span>
                </button>
                <div x-show="phase === 'settled'" style="font-size:11px; color:#94a3b8;">窗口将在倒计时结束后自动关闭</div>
            </div>
        </div>
    </div>
</div>



<style>
    @keyframes horse-run {
        0% {
            transform: translateX(-2px);
        }

        100% {
            transform: translateX(2px);
        }
    }

    @keyframes pulse-horse {

        0%,
        100% {
            box-shadow: 0 4px 20px rgba(245, 158, 11, .5);
        }

        50% {
            box-shadow: 0 4px 32px rgba(245, 158, 11, .9);
        }
    }
</style>

<script>
    /**
     * 赛马竞猜悬浮按钮 Alpine 组件（拖动 + localStorage 位置持久化）
     */
    function horseRaceFab() {
        const STORAGE_KEY = 'horse_race_fab_pos';
        const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || 'null');
        return {
            visible: false,
            posX: saved?.x ?? 80,
            posY: saved?.y ?? 90,
            dragging: false,
            _startX: 0,
            _startY: 0,
            _origX: 0,
            _origY: 0,
            _moved: false,

            startDrag(e) {
                this.dragging = true;
                this._moved = false;
                this._startX = e.clientX;
                this._startY = e.clientY;
                this._origX = this.posX;
                this._origY = this.posY;
                e.currentTarget.setPointerCapture?.(e.pointerId);
            },

            onDrag(e) {
                if (!this.dragging) return;
                const dx = e.clientX - this._startX;
                const dy = e.clientY - this._startY;
                if (Math.abs(dx) > 3 || Math.abs(dy) > 3) this._moved = true;
                // right 定位：往右拖 dx>0 → right 减小；bottom 定位：往下拖 dy>0 → bottom 减小
                this.posX = Math.max(4, Math.min(window.innerWidth - 132, this._origX - dx));
                this.posY = Math.max(4, Math.min(window.innerHeight - 132, this._origY - dy));
            },

            endDrag(e) {
                if (!this.dragging) return;
                this.dragging = false;
                localStorage.setItem(STORAGE_KEY, JSON.stringify({
                    x: this.posX,
                    y: this.posY
                }));
                if (!this._moved) this.openPanel();
            },

            openPanel() {
                const panel = document.getElementById('horse-race-panel');
                if (panel) Alpine.$data(panel).openFromHall();
            },
        };
    }

    /**
     * 赛马竞猜主面板 Alpine 组件
     */
    function horseRacePanel() {
        return {
            show: false,
            phase: 'idle', // idle | betting | running | settled

            raceId: null,
            totalSeconds: 90,
            countdown: 90,
            countdownTimer: null,

            settledCountdown: 10,
            settledTimer: null,

            // 马匹列表（含实时赔率）
            horses: [],
            positions: {}, // 跑马进度 {horse_id: 0~100}
            leaderId: null,

            // 注池
            totalPool: 0,

            // 本人下注
            myBet: false,
            myBetHorseId: null,
            myBetHorseName: '',
            myBetAmount: 0,

            // 下注表单
            selectedHorse: null,
            betAmount: 100,
            minBet: 100,
            maxBet: 100000,
            submitting: false,

            // 结算结果
            winnerName: '',
            winnerEmoji: '',
            myWon: false,
            myPayout: 0,

            // 历史记录
            history: [],

            /**
             * 读取赛马接口 JSON；若后端返回了 HTML/警告页，则抛出可诊断错误。
             *
             * @param {string} url 请求地址
             * @param {RequestInit} options fetch 选项
             * @returns {Promise<any>}
             */
            async requestJson(url, options = {}) {
                const requestUrl = new URL(url, window.location.origin);
                requestUrl.searchParams.set('_ts', Date.now().toString());

                const response = await fetch(requestUrl.toString(), {
                    cache: 'no-store',
                    headers: {
                        'Accept': 'application/json',
                        'Cache-Control': 'no-cache',
                        'Pragma': 'no-cache',
                        ...(options.headers || {}),
                    },
                    ...options,
                });
                const rawText = await response.text();

                try {
                    return JSON.parse(rawText);
                } catch (error) {
                    const preview = rawText.slice(0, 160).replace(/\s+/g, ' ').trim();
                    throw new Error(`赛马接口未返回 JSON（${response.status}）: ${preview}`);
                }
            },

            /**
             * 同步全局聊天上下文中的金币余额，供弹窗右上角与其他面板共用。
             */
            syncUserGold(jjb) {
                if (jjb === undefined || jjb === null) return;
                if (!window.chatContext) return;
                window.chatContext.userJjb = Number(jjb);
                window.chatContext.myGold = Number(jjb);
            },

            /**
             * 获取当前选中马匹的预览名称（用于按钮文字）
             */
            get myBetHorsePreviewName() {
                if (!this.selectedHorse) return '';
                const h = this.horses.find(h => h.id === this.selectedHorse);
                return h ? h.emoji + h.name : '';
            },

            /**
             * 获取快捷下注金额数组
             */
            get quickBetAmounts() {
                const min = this.minBet || 100;
                const max = this.maxBet || 100000;
                
                // 预设候选倍数，尽量生成美观的数字
                const candidates = [1, 2, 5, 10, 20, 50, 100, 200, 500, 1000];
                let steps = candidates.map(m => min * m).filter(v => v >= min && v < max);
                steps.push(max);
                steps = [...new Set(steps)].sort((a, b) => a - b);

                if (steps.length >= 5) {
                    // 如果候选值足够多，均匀采样，确保首尾是最小值和最大值
                    return [
                        steps[0],
                        steps[Math.floor((steps.length - 1) * 0.25)],
                        steps[Math.floor((steps.length - 1) * 0.5)],
                        steps[Math.floor((steps.length - 1) * 0.75)],
                        steps[steps.length - 1]
                    ];
                } else {
                    // 如果候选值不足5个（范围太小），通过线性插值补齐
                    while (steps.length < 5) {
                        let maxGap = 0;
                        let insertIdx = -1;
                        for (let i = 0; i < steps.length - 1; i++) {
                            if (steps[i+1] - steps[i] > maxGap) {
                                maxGap = steps[i+1] - steps[i];
                                insertIdx = i;
                            }
                        }
                        if (insertIdx === -1) break;
                        let newVal = Math.floor((steps[insertIdx] + steps[insertIdx+1]) / 2);
                        if (newVal > 100) newVal = Math.floor(newVal / 10) * 10;
                        steps.splice(insertIdx + 1, 0, newVal);
                    }
                    return steps;
                }
            },

            /**
             * 开赛：填充场次数据并开始倒计时
             */
            openRace(data) {
                this.phase = 'betting';
                this.raceId = data.race_id;
                this.countdown = data.bet_seconds || 90;
                this.totalSeconds = this.countdown;
                this.horses = data.horses || [];
                this.totalPool = data.total_pool || 0;
                this.myBet = false;
                this.myBetHorseId = null;
                this.myBetHorseName = '';
                this.myBetAmount = 0;
                this.selectedHorse = null;
                this.betAmount = 100;
                this.positions = {};
                this.leaderId = null;
                this.show = true;

                this.loadCurrentRace();
                this.startCountdown();
                this.updateFab(true);
            },

            /**
             * 从接口获取当前场次状态（我的下注、注池赔率）
             */
            async loadCurrentRace() {
                try {
                    const data = await this.requestJson('/horse-race/current');
                    // 每次打开或刷新当前场次时，都先同步右上角金币余额。
                    this.syncUserGold(data.jjb);
                    if (data.race) {
                        this.horses = data.race.horses || this.horses;
                        this.totalPool = data.race.total_pool || 0;
                        this.minBet = data.race.min_bet || 100;
                        this.maxBet = data.race.max_bet || 100000;
                        if (data.race.my_bet) {
                            this.myBet = true;
                            this.myBetHorseId = data.race.my_bet.horse_id;
                            this.myBetAmount = data.race.my_bet.amount;
                            this.selectedHorse = data.race.my_bet.horse_id;
                            const h = this.horses.find(h => h.id === this.myBetHorseId);
                            this.myBetHorseName = h ? h.emoji + h.name : '';
                        }
                    } else {
                        this.phase = 'idle';
                        this.raceId = null;
                        this.horses = [];
                        this.totalPool = 0;
                        this.countdown = 0;
                    }
                } catch {}
            },

            /**
             * 启动倒计时
             */
            startCountdown() {
                clearInterval(this.countdownTimer);
                this.countdownTimer = setInterval(() => {
                    this.countdown--;
                    if (this.countdown <= 0) {
                        clearInterval(this.countdownTimer);
                        this.phase = 'running';
                    }
                }, 1000);
            },

            /**
             * 提交下注
             */
            async submitBet() {
                if (!this.selectedHorse || this.betAmount < 100 || this.submitting) return;
                this.submitting = true;

                try {
                    const res = await fetch('/horse-race/bet', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                            'Accept': 'application/json',
                            'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]')?.content,
                        },
                        body: JSON.stringify({
                            race_id: this.raceId,
                            horse_id: this.selectedHorse,
                            amount: this.betAmount,
                        }),
                    });
                    const data = await res.json();

                    if (data.ok) {
                        this.myBet = true;
                        this.myBetHorseId = data.horse_id;
                        this.selectedHorse = data.horse_id;
                        this.myBetAmount = data.amount;
                        const h = this.horses.find(h => h.id === data.horse_id);
                        this.myBetHorseName = h ? h.emoji + h.name : '';
                        await this.loadCurrentRace();
                    } else {
                        window.chatDialog?.alert(data.message || '下注失败', '提示', '#ef4444');
                    }
                } catch {
                    window.chatDialog?.alert('网络异常，请稍后重试。', '错误', '#ef4444');
                }

                this.submitting = false;
            },

            /**
             * 接收跑马进度更新
             */
            updateProgress(data) {
                this.phase = 'running';
                // 确保响应式更新
                this.positions = {
                    ...this.positions,
                    ...(data.positions || {})
                };
                this.leaderId = data.leader_id;
            },

            /**
             * 显示结算结果
             */
            showResult(data) {
                clearInterval(this.countdownTimer);
                clearInterval(this.settledTimer);
                this.phase = 'settled';
                this.show = true;
                this.totalPool = data.total_pool || this.totalPool;

                // 找出获胜马匹信息
                const winner = this.horses.find(h => h.id === data.winner_horse_id);
                this.winnerName = winner ? winner.emoji + winner.name : data.winner_name || '未知';
                this.winnerEmoji = winner ? winner.emoji : '🐎';

                // 判断本人是否中奖
                if (this.myBet && this.myBetHorseId === data.winner_horse_id) {
                    this.myWon = true;
                    // 结算广播已携带冠军注池与可派奖池，这里按后端同公式还原个人赔付展示值。
                    const winnerPool = Number(data.winner_pool || 0);
                    const distributablePool = Number(data.distributable_pool || 0);
                    this.myPayout = winnerPool > 0
                        ? Math.round(distributablePool * (Number(this.myBetAmount || 0) / winnerPool))
                        : 0;
                } else {
                    this.myWon = false;
                    this.myPayout = 0;
                }

                this.updateFab(false);
                this.loadHistory();

                // 10秒倒计时自动关闭结果弹窗
                this.settledCountdown = 10;
                this.settledTimer = setInterval(() => {
                    this.settledCountdown--;
                    if (this.settledCountdown <= 0) {
                        clearInterval(this.settledTimer);
                        this.close();
                    }
                }, 1000);
            },

            /**
             * 加载历史记录
             */
            async loadHistory() {
                try {
                    const data = await this.requestJson('/horse-race/history');
                    this.history = (data.history || []).reverse();
                } catch {}
            },

            /**
             * 更新悬浮按钮显示状态
             */
            updateFab(visible) {
                const fab = document.getElementById('horse-race-fab');
                if (fab) Alpine.$data(fab).visible = visible;
            },

            /**
             * 关闭面板
             */
            close() {
                this.show = false;
                clearInterval(this.settledTimer);
                if (this.phase === 'betting') {
                    this.updateFab(true);
                }
            },

            /**
             * 从游戏大厅入口打开面板：先重新请求当前场次最新状态，再显示面板。
             * 解决游戏大厅展示‚押注中‚但面板状态降旧导致提交报错的问题。
             */
            async openFromHall() {
                try {
                    const data = await this.requestJson('/horse-race/current');
                    this.syncUserGold(data.jjb);
                    if (data.race) {
                        const race = data.race;
                        this.raceId = race.id;
                        this.horses = race.horses || [];
                        this.totalPool = race.total_pool || 0;
                        this.minBet = race.min_bet || 100;
                        this.maxBet = race.max_bet || 100000;

                        // 更新本人下注状态
                        if (race.my_bet) {
                            this.myBet = true;
                            this.myBetHorseId = race.my_bet.horse_id;
                            this.myBetAmount = race.my_bet.amount;
                            const h = this.horses.find(h => h.id === race.my_bet.horse_id);
                            this.myBetHorseName = h ? h.emoji + h.name : '';
                        } else {
                            this.myBet = false;
                            this.myBetHorseId = null;
                            this.myBetHorseName = '';
                            this.myBetAmount = 0;
                        }

                        // 同步阶段和倒计时
                        if (race.status === 'betting' && (race.seconds_left ?? 0) > 0) {
                            this.phase = 'betting';
                            this.countdown = race.seconds_left;
                            this.totalSeconds = race.seconds_left;
                            this.startCountdown();
                        } else if (race.status === 'running') {
                            this.phase = 'running';
                        } else {
                            this.phase = 'settled';
                        }
                    } else {
                        // 当前无进行中场次，重置状态
                        clearInterval(this.countdownTimer);
                        this.raceId = null;
                        this.horses = [];
                        this.totalPool = 0;
                        this.myBet = false;
                        this.myBetHorseId = null;
                        this.myBetHorseName = '';
                        this.myBetAmount = 0;
                        this.selectedHorse = null;
                        this.phase = 'idle';
                        this.countdown = 0;
                    }
                } catch (e) {
                    console.warn('[\u8d5b\u9a6c] openFromHall 失\u8d25', e);
                }
                this.show = true;
            },
        };
    }

    // ─── WebSocket 监听 ──────────────────────────────────────────────

    /** 收到开赛事件：弹出押注面板 */
    window.addEventListener('chat:horse.opened', (e) => {
        const panel = document.getElementById('horse-race-panel');
        if (panel) Alpine.$data(panel).openRace(e.detail);
    });

    /** 收到跑马进度事件：更新赛道 */
    window.addEventListener('chat:horse.progress', (e) => {
        const panel = document.getElementById('horse-race-panel');
        if (panel) Alpine.$data(panel).updateProgress(e.detail);
    });

    /** 收到结算事件：展示结果 */
    window.addEventListener('chat:horse.settled', (e) => {
        const panel = document.getElementById('horse-race-panel');
        if (panel) Alpine.$data(panel).showResult(e.detail);
    });

    /** 页面加载时恢复进行中的场次 */
    document.addEventListener('DOMContentLoaded', () => window.deferChatGameBootstrap(async () => {
        try {
            const panel = document.getElementById('horse-race-panel');
            const histData = panel ? await Alpine.$data(panel).requestJson('/horse-race/history') : { history: [] };
            const fab = document.getElementById('horse-race-fab');

            if (panel) {
                Alpine.$data(panel).history = (histData.history || []).reverse();
            }

            const curData = panel ? await Alpine.$data(panel).requestJson('/horse-race/current') : { race: null };
            if (panel) {
                Alpine.$data(panel).syncUserGold(curData.jjb);
            }

            // 游戏可访问则常驻显示 FAB（与占卜一致）
            if (fab) Alpine.$data(fab).visible = true;

            if (curData.race && panel) {
                const race = curData.race;
                const seconds = race.seconds_left || 0;
                const panelData = Alpine.$data(panel);

                panelData.raceId = race.id;
                panelData.horses = race.horses || [];
                panelData.totalPool = race.total_pool || 0;

                if (race.my_bet) {
                    panelData.myBet = true;
                    panelData.myBetHorseId = race.my_bet.horse_id;
                    panelData.myBetAmount = race.my_bet.amount;
                    const h = panelData.horses.find(h => h.id === race.my_bet.horse_id);
                    panelData.myBetHorseName = h ? h.emoji + h.name : '';
                }

                if (race.status === 'betting' && seconds > 0) {
                    panelData.phase = 'betting';
                    panelData.countdown = seconds;
                } else if (race.status === 'running') {
                    panelData.phase = 'running';
                } else {
                    panelData.phase = 'settled';
                }
            } else if (panel) {
                const panelData = Alpine.$data(panel);
                panelData.phase = 'idle';
                panelData.raceId = null;
                panelData.horses = [];
                panelData.totalPool = 0;
                panelData.countdown = 0;
            }
        } catch (e) {
            console.warn('[赛马] 初始化失败', e);
        }
    }));
</script>
