define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {

    var Controller = {
        index: function () {
            Table.api.init({
                extend: {
                    index_url: 'history/index' + location.search,
                    add_url: 'history/add',
                    edit_url: 'history/edit',
                    del_url: 'history/del',
                    multi_url: 'history/multi',
                    import_url: 'history/import',
                    table: 'history',
                }
            });

            var table = $("#table");

            // 从后端获取颜色和生肖映射
            Controller.api.loadColorMap(function () {
                Controller.api.loadAnimalMap(function () {
                    table.bootstrapTable({
                        url: $.fn.bootstrapTable.defaults.extend.index_url,
                        pk: 'expect',
                        sortName: 'expect',
                        columns: [
                            [
                                {checkbox: true},
                                {field: 'expect', title: __('Expect')},
                                {field: 'num1', title: __('Num1'), formatter: Controller.api.formatter.numBall},
                                {field: 'num2', title: __('Num2'), formatter: Controller.api.formatter.numBall},
                                {field: 'num3', title: __('Num3'), formatter: Controller.api.formatter.numBall},
                                {field: 'num4', title: __('Num4'), formatter: Controller.api.formatter.numBall},
                                {field: 'num5', title: __('Num5'), formatter: Controller.api.formatter.numBall},
                                {field: 'num6', title: __('Num6'), formatter: Controller.api.formatter.numBall},
                                {field: 'num7', title: __('Num7'), formatter: Controller.api.formatter.numBall},
                                {field: 'openTime', title: __('OpenTime'), operate:'RANGE', addclass:'datetimerange', autocomplete:false},
                                {field: 'search_day', title: '每月日号', visible: false, searchable: true, operate: '=', style: 'width:80px;', extend: 'min="1" max="31" placeholder="1-31"'}
                            ]
                        ]
                    });

                    Table.api.bindevent(table);
                });
            });

            // 遗漏号码按钮事件
            $(document).off('click', '.btn-missingnum').on('click', '.btn-missingnum', function () {
                Controller.api.showMissingNumDialog();
            });

            // 走势图按钮事件
            $(document).off('click', '.btn-trend').on('click', '.btn-trend', function () {
                Controller.api.showTrendDialog();
            });

            // 冷热分析按钮事件
            $(document).off('click', '.btn-hotcold').on('click', '.btn-hotcold', function () {
                Controller.api.showHotColdDialog();
            });

            // 波色分析按钮事件
            $(document).off('click', '.btn-colorwave').on('click', '.btn-colorwave', function () {
                Controller.api.showAnalysisDialog('colorWave');
            });
            // 生肖分析按钮事件
            $(document).off('click', '.btn-zodiac').on('click', '.btn-zodiac', function () {
                Controller.api.showAnalysisDialog('zodiac');
            });
            // 奇偶分析按钮事件
            $(document).off('click', '.btn-oddeven').on('click', '.btn-oddeven', function () {
                Controller.api.showAnalysisDialog('oddEven');
            });
            // 大小分析按钮事件
            $(document).off('click', '.btn-bigsmall').on('click', '.btn-bigsmall', function () {
                Controller.api.showAnalysisDialog('bigSmall');
            });
            // 和值分析按钮事件
            $(document).off('click', '.btn-sumchart').on('click', '.btn-sumchart', function () {
                Controller.api.showSumDialog();
            });
            // 连号分析按钮事件
            $(document).off('click', '.btn-consecutive').on('click', '.btn-consecutive', function () {
                Controller.api.showConsecutiveDialog();
            });
            // 尾数分析按钮事件
            $(document).off('click', '.btn-tailnums').on('click', '.btn-tailnums', function () {
                Controller.api.showAnalysisDialog('tailNumbers');
            });

            // 特码冷热按钮事件
            $(document).off('click', '.btn-specialhotcold').on('click', '.btn-specialhotcold', function () {
                Controller.api.showSpecialHotColdDialog();
            });

            // 综合统计面板按钮事件
            $(document).off('click', '.btn-dashboard').on('click', '.btn-dashboard', function () {
                Controller.api.showDashboard();
            });

            // 筛号器按钮事件
            $(document).off('click', '.btn-numberfilter').on('click', '.btn-numberfilter', function () {
                Controller.api.showNumberFilterDialog();
            });

            // 预测号码按钮事件
            $(document).off('click', '.btn-predict').on('click', '.btn-predict', function () {
                Controller.api.showPredictDialog();
            });

            // 正码关联预测按钮事件
            $(document).off('click', '.btn-normal-relation').on('click', '.btn-normal-relation', function () {
                Controller.api.showNormalRelationDialog();
            });
        },
        add: function () {
            Controller.api.bindevent();
        },
        edit: function () {
            Controller.api.bindevent();
        },
        api: {
            colorMap: {},
            colorMapLoaded: false,
            animalMap: {},
            animalMapLoaded: false,

            /**
             * 从后端加载颜色映射并缓存
             */
            loadColorMap: function (callback) {
                if (Controller.api.colorMapLoaded) {
                    callback();
                    return;
                }
                $.ajax({
                    url: 'num/getColorMap',
                    type: 'GET',
                    dataType: 'json',
                    success: function (ret) {
                        if (ret.code == 1) {
                            Controller.api.colorMap = ret.msg;
                        }
                        Controller.api.colorMapLoaded = true;
                        callback();
                    }
                });
            },

            /**
             * 从后端加载生肖映射并缓存
             */
            loadAnimalMap: function (callback) {
                if (Controller.api.animalMapLoaded) {
                    callback();
                    return;
                }
                $.ajax({
                    url: 'num/getAnimalMap',
                    type: 'GET',
                    dataType: 'json',
                    success: function (ret) {
                        if (ret.code == 1) {
                            Controller.api.animalMap = ret.msg;
                        }
                        Controller.api.animalMapLoaded = true;
                        callback();
                    }
                });
            },

            /**
             * 根据数字从映射表中获取生肖
             */
            getAnimalByNum: function (num) {
                return Controller.api.animalMap[num] || '';
            },

            /**
             * 根据数字从映射表中获取颜色
             */
            getColorByNum: function (num) {
                var color = Controller.api.colorMap[num];
                if (!color) return '#95a5a6';
                // 后端返回中文波色，前端映射为CSS颜色
                if (color.indexOf('红') !== -1) return '#e74c3c';
                if (color.indexOf('蓝') !== -1) return '#3498db';
                if (color.indexOf('绿') !== -1) return '#2ecc71';
                return '#95a5a6';
            },

            formatter: {
                numBall: function (value, row, index) {
                    if (value === null || value === undefined || value === '') return '';
                    var num = parseInt(value);
                    var color = Controller.api.getColorByNum(num);
                    var animal = Controller.api.getAnimalByNum(num);
                    var html = '<div style="text-align:center;">' +
                        '<span class="num-ball" style="display:inline-block;width:32px;height:32px;line-height:32px;text-align:center;border-radius:50%;color:#fff;background-color:' + color + ';font-weight:bold;">' + value + '</span>';
                    if (animal) {
                        html += '<div style="font-size:10px;color:#666;line-height:1.2;">' + animal + '</div>';
                    }
                    html += '</div>';
                    return html;
                }
            },

            /**
             * 显示冷热分析弹窗
             */
            showHotColdDialog: function () {
                var html = '<div style="padding:20px;">' +
                    '<div class="form-group" style="border-bottom:1px solid #eee;padding-bottom:10px;margin-bottom:10px;">' +
                    '  <label style="margin-right:15px;">' + __('Query Type') + '：</label>' +
                    '  <label class="radio-inline" style="margin-right:15px;">' +
                    '    <input type="radio" name="hc-type" value="all" checked> ' + __('All Numbers') +
                    '  </label>' +
                    '  <label class="radio-inline">' +
                    '    <input type="radio" name="hc-type" value="special"> ' + __('Special Only') +
                    '  </label>' +
                    '</div>' +
                    '<div class="form-group">' +
                    '  <label>' + __('Query Periods') + '：</label>' +
                    '  <input type="number" id="hc-periods" class="form-control" value="30" min="10" max="100" style="width:120px;display:inline-block;">' +
                    '  <button class="btn btn-primary" id="btn-hc-query" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
                    '</div>' +
                    '<div id="hc-result" style="margin-top:15px;overflow-x:auto;"></div>' +
                    '</div>';

                Layer.open({
                    type: 1,
                    title: __('Hot/Cold Analysis'),
                    area: ['700px', '600px'],
                    content: html,
                    shadeClose: true,
                    success: function (layero, index) {
                        $('#btn-hc-query', layero).on('click', function () {
                            var periods = parseInt($('#hc-periods', layero).val()) || 30;
                            var type = $('input[name="hc-type"]:checked', layero).val();
                            Controller.api.queryHotCold(periods, type, layero);
                        });
                        $('input[name="hc-type"]', layero).on('change', function () {
                            var periods = parseInt($('#hc-periods', layero).val()) || 30;
                            Controller.api.queryHotCold(periods, $(this).val(), layero);
                        });
                    }
                });
            },

            queryHotCold: function (periods, type, layero) {
                var $btn = $('#btn-hc-query', layero);
                $btn.prop('disabled', true);
                $('#hc-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
                $.ajax({
                    url: 'history/hotColdNumbers',
                    type: 'GET',
                    data: {periods: periods, type: type},
                    dataType: 'json',
                    success: function (ret) {
                        if (ret.code == 1) {
                            Controller.api.renderHotCold(ret.data, layero);
                        } else {
                            $('#hc-result', layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
                        }
                    },
                    error: function () {
                        $('#hc-result', layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
                    },
                    complete: function () {
                        $btn.prop('disabled', false);
                    }
                });
            },

            renderHotCold: function (data, layero) {
                var getColor = function (num) {
                    var color = data.all.find(function (item) { return item.num === num; });
                    if (!color || !color.color) return '#95a5a6';
                    if (color.color.indexOf('红') !== -1) return '#e74c3c';
                    if (color.color.indexOf('蓝') !== -1) return '#3498db';
                    if (color.color.indexOf('绿') !== -1) return '#2ecc71';
                    return '#95a5a6';
                };

                var getAnimal = function (num) {
                    var item = data.all.find(function (item) { return item.num === num; });
                    return item ? (item.animal || '') : '';
                };

                var renderSection = function (title, items, icon) {
                    var html = '<div style="margin-bottom:15px;"><h4 style="margin:0 0 8px 0;border-bottom:1px solid #eee;padding-bottom:5px;">' + icon + ' ' + title + '</h4>';
                    html += '<div style="display:flex;flex-wrap:wrap;gap:8px;">';
                    for (var i = 0; i < items.length; i++) {
                        var item = items[i];
                        var color = getColor(item.num);
                        var animal = getAnimal(item.num);
                        html += '<div style="text-align:center;background:#f9f9f9;padding:8px;border-radius:6px;min-width:70px;">' +
                            '<span style="display:inline-block;width:36px;height:36px;line-height:36px;text-align:center;border-radius:50%;color:#fff;background-color:' + color + ';font-weight:bold;">' + item.num + '</span>' +
                            '<div style="margin-top:4px;font-size:10px;color:#666;">' + (animal ? animal + '<br>' : '') + '<b>' + item.count + '</b> (' + item.percent + '%)</div>' +
                            '</div>';
                    }
                    html += '</div></div>';
                    return html;
                };

                var html = '<div style="padding:10px;">' +
                    renderSection('热号 Top 10', data.hot, '<span style="color:#e74c3c;">&#x1F525;</span>') +
                    renderSection('冷号 Top 10', data.cold, '<span style="color:#3498db;">&#x2744;</span>') +
                    '</div>';

                $('#hc-result', layero).html(html);
            },

            /**
             * 显示走势图弹窗
             */
            showTrendDialog: function () {
                var html = '<div style="padding:20px;">' +
                    '<div class="form-group" style="border-bottom:1px solid #eee;padding-bottom:10px;margin-bottom:10px;">' +
                    '  <label style="margin-right:15px;">' + __('Query Type') + '：</label>' +
                    '  <label class="radio-inline" style="margin-right:15px;">' +
                    '    <input type="radio" name="trend-type" value="all" checked> ' + __('All Numbers') +
                    '  </label>' +
                    '  <label class="radio-inline">' +
                    '    <input type="radio" name="trend-type" value="special"> ' + __('Special Only') +
                    '  </label>' +
                    '</div>' +
                    '<div class="form-group">' +
                    '  <label>' + __('Query Periods') + '：</label>' +
                    '  <input type="number" id="trend-periods" class="form-control" value="30" min="10" max="100" style="width:120px;display:inline-block;">' +
                    '  <button class="btn btn-primary" id="btn-trend-query" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
                    '</div>' +
                    '<div id="trend-result" style="margin-top:15px;overflow-x:auto;"></div>' +
                    '</div>';

                Layer.open({
                    type: 1,
                    title: __('Trend Chart'),
                    area: ['90%', '80%'],
                    content: html,
                    shadeClose: false,
                    maxmin: true,
                    success: function (layero, index) {
                        $('#btn-trend-query', layero).on('click', function () {
                            var periods = parseInt($('#trend-periods', layero).val()) || 30;
                            var type = $('input[name="trend-type"]:checked', layero).val();
                            Controller.api.queryTrend(periods, type, layero);
                        });
                        $('input[name="trend-type"]', layero).on('change', function () {
                            var periods = parseInt($('#trend-periods', layero).val()) || 30;
                            Controller.api.queryTrend(periods, $(this).val(), layero);
                        });
                    }
                });
            },

            /**
             * 查询走势图
             */
            queryTrend: function (periods, type, layero) {
                var $btn = $('#btn-trend-query', layero);
                $btn.prop('disabled', true);
                $('#trend-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
                $.ajax({
                    url: 'history/trendData',
                    type: 'GET',
                    data: {periods: periods, type: type},
                    dataType: 'json',
                    success: function (ret) {
                        if (ret.code == 1) {
                            Controller.api.renderTrend(ret.data, type, layero);
                        } else {
                            $('#trend-result', layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
                        }
                    },
                    error: function () {
                        $('#trend-result', layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
                    },
                    complete: function () {
                        $btn.prop('disabled', false);
                    }
                });
            },

            /**
             * 渲染走势图（ECharts 折线图）
             */
            renderTrend: function (data, type, layero) {
                var expects = data.expects;
                var rows = data.data;
                var colorMap = data.colorMap;

                if (!expects || expects.length === 0) {
                    $('#trend-result', layero).html('<div class="alert alert-info">' + __('No data available') + '</div>');
                    return;
                }

                var getColor = function (num) {
                    var color = colorMap[num];
                    if (!color) return '#95a5a6';
                    if (color.indexOf('红') !== -1) return '#e74c3c';
                    if (color.indexOf('蓝') !== -1) return '#3498db';
                    if (color.indexOf('绿') !== -1) return '#2ecc71';
                    return '#95a5a6';
                };

                $('#trend-result', layero).html('<div id="trend-chart" style="width:100%;height:500px;"></div>');

                var chartDom = document.getElementById('trend-chart');
                var myChart = echarts.init(chartDom);

                var series = [];
                if (type === 'special') {
                    series = [{
                        name: '特码',
                        type: 'line',
                        data: rows.map(function (r) { return r.num7; }),
                        smooth: false,
                        symbol: 'circle',
                        symbolSize: 8,
                        lineStyle: { width: 2 },
                        itemStyle: {
                            color: function (params) {
                                var num = params.data;
                                return getColor(num);
                            }
                        },
                        label: {
                            show: true,
                            position: 'top',
                            fontSize: 11,
                            color: '#333'
                        }
                    }];
                } else {
                    var numFields = [];
                    for (var c = 1; c <= 7; c++) {
                        numFields.push('num' + c);
                    }
                    var colors = ['#e74c3c', '#3498db', '#2ecc71', '#f39c12', '#9b59b6', '#1abc9c', '#e67e22'];
                    for (var f = 0; f < numFields.length; f++) {
                        (function (idx) {
                            series.push({
                                name: '第' + (idx + 1) + '码',
                                type: 'line',
                                data: rows.map(function (r) { return r[numFields[idx]]; }),
                                smooth: false,
                                symbol: 'circle',
                                symbolSize: 6,
                                lineStyle: { width: 2 },
                                itemStyle: { color: colors[idx] },
                                label: {
                                    show: true,
                                    position: 'top',
                                    fontSize: 10,
                                    color: '#333'
                                }
                            });
                        })(f);
                    }
                }

                var option = {
                    title: { text: type === 'special' ? '特码走势' : '全部号码走势', left: 'center', textStyle: { fontSize: 14 } },
                    tooltip: { trigger: 'axis', formatter: function (params) {
                        var tip = '期号: ' + params[0].axisValueLabel + '<br/>';
                        for (var i = 0; i < params.length; i++) {
                            tip += params[i].seriesName + ': <b>' + params[i].data + '</b><br/>';
                        }
                        return tip;
                    }},
                    legend: { bottom: 10, data: series.map(function (s) { return s.name; }) },
                    grid: { left: 40, right: 20, bottom: 50, top: 40 },
                    xAxis: { type: 'category', data: expects, axisLabel: { rotate: 45, fontSize: 10 } },
                    yAxis: { type: 'value', min: 0, max: 50, splitLine: { show: true, lineStyle: { type: 'dashed' } } },
                    dataZoom: [{ type: 'slider', bottom: 30, height: 20, start: 0, end: 100 }],
                    series: series
                };

                myChart.setOption(option);
                window.addEventListener('resize', function () { myChart.resize(); });
            },

            /**
             * 显示遗漏号码分析弹窗
             */
            showMissingNumDialog: function () {
                var html = '<div style="padding:20px;">' +
                    '<div class="form-group" style="border-bottom:1px solid #eee;padding-bottom:10px;margin-bottom:10px;">' +
                    '  <label style="margin-right:15px;">' + __('Query Type') + '：</label>' +
                    '  <label class="radio-inline" style="margin-right:15px;">' +
                    '    <input type="radio" name="missing-type" value="all" checked> ' + __('All Numbers') +
                    '  </label>' +
                    '  <label class="radio-inline">' +
                    '    <input type="radio" name="missing-type" value="special"> ' + __('Special Only') +
                    '  </label>' +
                    '</div>' +
                    '<div class="form-group">' +
                    '  <label>' + __('Query Periods') + '：</label>' +
                    '  <input type="number" id="missing-periods" class="form-control" value="10" min="1" max="100" style="width:120px;display:inline-block;">' +
                    '  <button class="btn btn-primary" id="btn-missing-query" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
                    '</div>' +
                    '<div id="missing-result" style="margin-top:15px;"></div>' +
                    '</div>';

                Layer.open({
                    type: 1,
                    title: __('Missing Number Analysis'),
                    area: ['650px', '550px'],
                    content: html,
                    shadeClose: true,
                    success: function (layero, index) {
                        // 绑定查询按钮事件
                        $('#btn-missing-query', layero).on('click', function () {
                            var periods = parseInt($('#missing-periods', layero).val()) || 10;
                            var type = $('input[name="missing-type"]:checked', layero).val();
                            Controller.api.queryMissingNum(periods, type, layero);
                        });
                        // 切换类型时自动查询
                        $('input[name="missing-type"]', layero).on('change', function () {
                            var periods = parseInt($('#missing-periods', layero).val()) || 10;
                            Controller.api.queryMissingNum(periods, $(this).val(), layero);
                        });
                    }
                });
            },

            /**
             * 查询遗漏号码
             */
            queryMissingNum: function (periods, type, layero) {
                // 确保颜色映射已加载
                if (!Controller.api.colorMapLoaded) {
                    Controller.api.loadColorMap(function () {
                        Controller.api._doQueryMissingNum(periods, type, layero);
                    });
                } else {
                    Controller.api._doQueryMissingNum(periods, type, layero);
                }
            },

            /**
             * 执行遗漏号码查询（内部方法）
             */
            _doQueryMissingNum: function (periods, type, layero) {
                var $btn = $('#btn-missing-query', layero);
                $btn.prop('disabled', true);
                $('#missing-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
                $.ajax({
                    url: 'history/missingNum',
                    type: 'GET',
                    data: {periods: periods, type: type},
                    dataType: 'json',
                    success: function (ret) {
                        if (ret.code == 1) {
                            Controller.api.renderMissingNum(ret.data, periods, layero);
                        } else {
                            $('#missing-result', layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
                        }
                    },
                    error: function () {
                        $('#missing-result', layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
                    },
                    complete: function () {
                        $btn.prop('disabled', false);
                    }
                });
            },

            /**
             * 渲染遗漏号码结果
             */
            renderMissingNum: function (data, periods, layero) {
                if (!data || data.length === 0) {
                    $('#missing-result', layero).html('<div class="alert alert-info">' + __('No missing numbers found') + '</div>');
                    return;
                }
                var container = $('<div style="display:flex;flex-wrap:wrap;gap:12px;"></div>');
                for (var i = 0; i < data.length; i++) {
                    var color = Controller.api.getColorByNum(data[i].num);
                    var animal = Controller.api.getAnimalByNum(data[i].num);
                    var $item = $('<div style="text-align:center;"></div>');
                    var $ball = $('<span class="num-ball"></span>').css({
                        'display': 'inline-block',
                        'width': '48px',
                        'height': '48px',
                        'line-height': '48px',
                        'text-align': 'center',
                        'border-radius': '50%',
                        'color': '#fff',
                        'background-color': color,
                        'font-weight': 'bold',
                        'font-size': '18px'
                    }).text(data[i].num);
                    var $content = $('<div></div>').append($ball);
                    if (animal) {
                        $content.append($('<div style="margin-top:3px;font-size:11px;color:#666;line-height:1.2;"></div>').text(animal));
                    }
                    $content.append($('<div style="margin-top:3px;font-size:12px;color:#666;"></div>').text(
                        __('Missing') + ' ' + data[i].omit + ' ' + __('periods')
                    ));
                    $item.append($content);
                    container.append($item);
                }
                $('#missing-result', layero).html('').append(container);
            },

            /**
             * 特码冷热列表（每期相对于前N期的冷热状态）
             */
            showSpecialHotColdDialog: function () {
                var html = '<div style="padding:20px;">' +
                    '<div class="form-group" style="border-bottom:1px solid #eee;padding-bottom:10px;margin-bottom:10px;">' +
                    '  <label>向前期数：</label>' +
                    '  <input type="number" id="shc-lookback" class="form-control" value="30" min="10" max="100" style="width:120px;display:inline-block;">' +
                    '  <button class="btn btn-primary" id="btn-shc-query" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
                    '</div>' +
                    '<div id="shc-result" style="margin-top:10px;max-height:500px;overflow-y:auto;"></div>' +
                    '</div>';

                Layer.open({
                    type: 1,
                    title: '特码冷热列表',
                    area: ['650px', '600px'],
                    content: html,
                    shadeClose: true,
                    success: function (layero, index) {
                        $('#btn-shc-query', layero).on('click', function () {
                            var lookback = parseInt($('#shc-lookback', layero).val()) || 30;
                            Controller.api.querySpecialHotCold(lookback, layero);
                        });
                        // 打开时自动查询
                        var lookback = parseInt($('#shc-lookback', layero).val()) || 30;
                        Controller.api.querySpecialHotCold(lookback, layero);
                    }
                });
            },

            querySpecialHotCold: function (lookback, layero) {
                var $btn = $('#btn-shc-query', layero);
                $btn.prop('disabled', true);
                $('#shc-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
                $.ajax({
                    url: 'history/specialHotColdAction',
                    type: 'GET',
                    data: {lookback: lookback},
                    dataType: 'json',
                    success: function (ret) {
                        if (ret.code == 1) {
                            Controller.api.renderSpecialHotCold(ret.data, lookback, layero);
                        } else {
                            $('#shc-result', layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
                        }
                    },
                    error: function () {
                        $('#shc-result', layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
                    },
                    complete: function () {
                        $btn.prop('disabled', false);
                    }
                });
            },

            renderSpecialHotCold: function (data, lookback, layero) {
                var getColor = function (num) {
                    return Controller.api.getColorByNum(num);
                };

                var statusTag = function (status) {
                    var map = {
                        'hot': '<span style="color:#e74c3c;font-weight:bold;">&#x1F525; 热号</span>',
                        'cold': '<span style="color:#3498db;font-weight:bold;">&#x2744; 冷号</span>',
                        'normal': '<span style="color:#f39c12;font-weight:bold;">&#x279C; 温号</span>',
                        'unknown': '<span style="color:#999;">数据不足</span>'
                    };
                    return map[status] || '';
                };

                // 新接口返回 {list: [...], current: {hot: [], cold: [], warm: []}}
                var listData = data.list || data;
                var current = data.current || null;

                if (!listData || listData.length === 0) {
                    $('#shc-result', layero).html('<div class="alert alert-info">暂无数据</div>');
                    return;
                }

                var html = '<div style="padding:0 5px;">';

                // 当前冷热号汇总区域
                if (current && (current.hot.length > 0 || current.cold.length > 0)) {
                    var renderNumBall = function (item) {
                        var color = getColor(item.num);
                        return '<span style="display:inline-block;width:30px;height:30px;line-height:30px;text-align:center;border-radius:50%;color:#fff;background-color:' + color + ';font-weight:bold;font-size:14px;" title="' + item.num + ' ×' + item.count + '">' + item.num + '</span>';
                    };

                    html += '<div style="background:#fafafa;border:1px solid #e0e0e0;border-radius:6px;padding:12px;margin-bottom:12px;">';
                    html += '<div style="font-size:13px;font-weight:bold;margin-bottom:8px;color:#333;"><i class="fa fa-bullseye"></i> 当前热号</div>';
                    html += '<div style="display:flex;flex-wrap:wrap;gap:6px;">';
                    for (var h = 0; h < current.hot.length; h++) {
                        html += renderNumBall(current.hot[h]);
                    }
                    if (current.hot.length === 0) {
                        html += '<span style="color:#999;font-size:12px;">暂无热号</span>';
                    }
                    html += '</div>';
                    html += '<div style="height:8px;"></div>';
                    html += '<div style="font-size:13px;font-weight:bold;margin-bottom:8px;color:#333;"><i class="fa fa-snowflake-o"></i> 当前冷号</div>';
                    html += '<div style="display:flex;flex-wrap:wrap;gap:6px;">';
                    for (var c = 0; c < current.cold.length; c++) {
                        html += renderNumBall(current.cold[c]);
                    }
                    if (current.cold.length === 0) {
                        html += '<span style="color:#999;font-size:12px;">暂无冷号</span>';
                    }
                    html += '</div>';
                    html += '</div>';
                }

                // 历史记录表格
                html += '<table class="table table-striped table-bordered table-hover" style="font-size:13px;">' +
                    '<thead><tr>' +
                    '<th style="text-align:center;width:120px;">期号</th>' +
                    '<th style="text-align:center;width:60px;">特码</th>' +
                    '<th style="text-align:center;width:80px;">冷热</th>' +
                    '<th style="text-align:center;width:60px;">次数</th>' +
                    '<th style="text-align:center;width:60px;">平均</th>' +
                    '<th style="text-align:center;width:60px;">排名</th>' +
                    '</tr></thead><tbody>';

                for (var i = 0; i < listData.length; i++) {
                    var item = listData[i];
                    var rowClass = '';
                    if (item.status === 'hot') rowClass = 'style="background:#fff5f5;"';
                    else if (item.status === 'cold') rowClass = 'style="background:#f5f8ff;"';

                    html += '<tr ' + rowClass + '>' +
                        '<td style="text-align:center;">' + item.expect + '</td>' +
                        '<td style="text-align:center;">' +
                        '<span style="display:inline-block;width:30px;height:30px;line-height:30px;text-align:center;border-radius:50%;color:#fff;background-color:' + getColor(item.specialNum) + ';font-weight:bold;font-size:14px;">' + item.specialNum + '</span>' +
                        '</td>' +
                        '<td style="text-align:center;">' + statusTag(item.status) + '</td>' +
                        '<td style="text-align:center;">' + item.count + '</td>' +
                        '<td style="text-align:center;">' + item.avgCount + '</td>' +
                        '<td style="text-align:center;">' + item.rank + '</td>' +
                        '</tr>';
                }

                html += '</tbody></table></div>';
                $('#shc-result', layero).html(html);
            },

            /**
             * 筛号器弹窗
             */
            showNumberFilterDialog: function () {
                var html = '<style>.btn-gray{background-color:#d2d2d2!important;border-color:#adadad!important;color:#999!important;}</style>' +
                    '<div style="padding:20px;">' +
                    '<div style="margin-bottom:15px;display:flex;gap:15px;align-items:center;flex-wrap:wrap;">' +
                    '  <div class="form-group" style="margin:0;">' +
                    '    <label>尾号筛选：</label>' +
                    '    <button class="btn btn-xs btn-primary btn-nf-add-tail"><i class="fa fa-plus"></i> 新增</button>' +
                    '  </div>' +
                    '  <button class="btn btn-default btn-nf-reset" style="margin-left:auto;"><i class="fa fa-refresh"></i> 重置</button>' +
                    '</div>' +
                    '<div id="nf-tail-list" style="margin-bottom:15px;display:flex;flex-wrap:wrap;gap:6px;"></div>' +
                    '<div style="margin-bottom:15px;">' +
                    '  <label style="margin-right:10px;">生肖：</label>' +
                    '  <div id="nf-zodiac" style="display:inline-flex;gap:6px;flex-wrap:wrap;">' +
                    '    <button class="btn btn-default btn-xs nf-zodiac" data-zodiac="鼠">鼠</button>' +
                    '    <button class="btn btn-default btn-xs nf-zodiac" data-zodiac="牛">牛</button>' +
                    '    <button class="btn btn-default btn-xs nf-zodiac" data-zodiac="虎">虎</button>' +
                    '    <button class="btn btn-default btn-xs nf-zodiac" data-zodiac="兔">兔</button>' +
                    '    <button class="btn btn-default btn-xs nf-zodiac" data-zodiac="龙">龙</button>' +
                    '    <button class="btn btn-default btn-xs nf-zodiac" data-zodiac="蛇">蛇</button>' +
                    '    <button class="btn btn-default btn-xs nf-zodiac" data-zodiac="马">马</button>' +
                    '    <button class="btn btn-default btn-xs nf-zodiac" data-zodiac="羊">羊</button>' +
                    '    <button class="btn btn-default btn-xs nf-zodiac" data-zodiac="猴">猴</button>' +
                    '    <button class="btn btn-default btn-xs nf-zodiac" data-zodiac="鸡">鸡</button>' +
                    '    <button class="btn btn-default btn-xs nf-zodiac" data-zodiac="狗">狗</button>' +
                    '    <button class="btn btn-default btn-xs nf-zodiac" data-zodiac="猪">猪</button>' +
                    '  </div>' +
                    '</div>' +
                    '<div style="margin-bottom:15px;">' +
                    '  <label style="margin-right:10px;">波色：</label>' +
                    '  <div id="nf-colorwrap" style="display:inline-flex;gap:6px;">' +
                    '    <button class="btn btn-default btn-xs nf-color-btn" data-color="红" style="color:#e74c3c;">红波</button>' +
                    '    <button class="btn btn-default btn-xs nf-color-btn" data-color="蓝" style="color:#3498db;">蓝波</button>' +
                    '    <button class="btn btn-default btn-xs nf-color-btn" data-color="绿" style="color:#2ecc71;">绿波</button>' +
                    '  </div>' +
                    '</div>' +
                    '<div style="margin-bottom:15px;">' +
                    '  <label style="margin-right:10px;">单双：</label>' +
                    '  <button class="btn btn-default btn-xs nf-parity" data-parity="单">单</button>' +
                    '  <button class="btn btn-default btn-xs nf-parity" data-parity="双">双</button>' +
                    '</div>' +
                    '<div style="margin-bottom:15px;">' +
                    '  <label style="margin-right:10px;">区间：</label>' +
                    '  <button class="btn btn-xs btn-primary btn-nf-add-range"><i class="fa fa-plus"></i> 新增</button>' +
                    '</div>' +
                    '<div id="nf-range-list" style="margin-bottom:15px;"></div>' +
                    '<div id="nf-numbers" style="display:flex;flex-wrap:wrap;gap:8px;margin-top:10px;"></div>' +
                    '</div>';

                Layer.open({
                    type: 1,
                    title: '筛号器',
                    area: ['700px', '600px'],
                    content: html,
                    shadeClose: true,
                    success: function (layero, index) {
                        // 手动屏蔽的号码列表
                        var blockedNums = [];

                        // 渲染号码网格
                        Controller.api.renderNumberFilterGrid(layero);

                        // 号码点击屏蔽
                        $('#nf-numbers', layero).on('click', '.nf-number', function () {
                            var num = parseInt($(this).data('num'));
                            var idx = blockedNums.indexOf(num);
                            if (idx === -1) {
                                blockedNums.push(num);
                            } else {
                                blockedNums.splice(idx, 1);
                            }
                            Controller.api.applyNumberFilters(layero, blockedNums);
                        });

                        // 新增尾号
                        $('.btn-nf-add-tail', layero).on('click', function () {
                            Controller.api.addTailRow(layero, 0);
                            Controller.api.applyNumberFilters(layero, blockedNums);
                        });

                        // 尾号输入 & 删除事件委托
                        $('#nf-tail-list', layero).on('input change', '.nf-tail-select', function () {
                            Controller.api.applyNumberFilters(layero, blockedNums);
                        });
                        $('#nf-tail-list', layero).on('click', '.nf-tail-del', function () {
                            $(this).closest('.nf-tail-row').remove();
                            Controller.api.applyNumberFilters(layero, blockedNums);
                        });

                        // 生肖按钮点击
                        $('.nf-zodiac', layero).on('click', function () {
                            var $btn = $(this);
                            $btn.toggleClass('btn-default').toggleClass('btn-gray');
                            Controller.api.applyNumberFilters(layero, blockedNums);
                        });

                        // 波色按钮点击
                        $('.nf-color-btn', layero).on('click', function () {
                            var $btn = $(this);
                            $btn.toggleClass('btn-default').toggleClass('btn-gray');
                            Controller.api.applyNumberFilters(layero, blockedNums);
                        });

                        // 单双按钮点击
                        $('.nf-parity', layero).on('click', function () {
                            var $btn = $(this);
                            $btn.toggleClass('btn-default').toggleClass('btn-gray');
                            Controller.api.applyNumberFilters(layero, blockedNums);
                        });

                        // 新增区间
                        $('.btn-nf-add-range', layero).on('click', function () {
                            Controller.api.addRangeRow(layero, 1, 49, 'include');
                            Controller.api.applyNumberFilters(layero, blockedNums);
                        });

                        // 区间输入 & 删除事件委托
                        $('#nf-range-list', layero).on('input change', '.nf-range-min, .nf-range-max', function () {
                            Controller.api.applyNumberFilters(layero, blockedNums);
                        });
                        $('#nf-range-list', layero).on('click', '.nf-range-mode', function () {
                            var $btn = $(this);
                            if ($btn.hasClass('btn-info')) {
                                $btn.removeClass('btn-info').addClass('btn-default');
                            } else {
                                $btn.removeClass('btn-default').addClass('btn-info');
                            }
                            Controller.api.applyNumberFilters(layero, blockedNums);
                        });
                        $('#nf-range-list', layero).on('click', '.nf-range-del', function () {
                            $(this).closest('.nf-range-row').remove();
                            Controller.api.applyNumberFilters(layero, blockedNums);
                        });

                        // 重置按钮
                        $('.btn-nf-reset', layero).on('click', function () {
                            blockedNums = [];
                            $('#nf-tail-list', layero).html('');
                            $('.nf-zodiac', layero).removeClass('btn-gray').addClass('btn-default');
                            $('.nf-color-btn', layero).removeClass('btn-gray').addClass('btn-default');
                            $('.nf-parity', layero).removeClass('btn-gray').addClass('btn-default');
                            $('#nf-range-list', layero).html('');
                            Controller.api.applyNumberFilters(layero, blockedNums);
                        });
                    }
                });
            },

            /**
             * 新增一行尾号筛选
             */
            addTailRow: function (layero, value) {
                var rowId = 'nf-tail-' + Date.now() + Math.random().toString(36).substr(2, 5);
                var opts = '<option value="">尾号</option>';
                for (var t = 0; t <= 9; t++) {
                    opts += '<option value="' + t + '"' + (t === value ? ' selected' : '') + '>' + t + '</option>';
                }
                var html = '<div class="nf-tail-row" id="' + rowId + '" style="display:inline-flex;align-items:center;gap:4px;">' +
                    '  <select class="form-control nf-tail-select" style="width:80px;display:inline-block;">' + opts + '</select>' +
                    '  <button class="btn btn-xs btn-danger nf-tail-del"><i class="fa fa-times"></i></button>' +
                    '</div>';
                $('#nf-tail-list', layero).append(html);
            },

            /**
             * 新增一行区间筛选
             */
            addRangeRow: function (layero, min, max, mode) {
                var rowId = 'nf-range-' + Date.now();
                var isInclude = mode === 'include';
                var html = '<div class="nf-range-row" id="' + rowId + '" style="display:flex;align-items:center;gap:6px;margin-bottom:6px;">' +
                    '  <span style="font-size:12px;color:#999;white-space:nowrap;">' + (isInclude ? '在区间' : '排除') + '</span>' +
                    '  <input type="number" class="form-control nf-range-min" value="' + min + '" min="1" max="49" style="width:65px;display:inline-block;">' +
                    '  <span style="margin:0 3px;">—</span>' +
                    '  <input type="number" class="form-control nf-range-max" value="' + max + '" min="1" max="49" style="width:65px;display:inline-block;">' +
                    '  <button class="btn btn-xs ' + (isInclude ? 'btn-info' : 'btn-default') + ' nf-range-mode" data-mode="' + mode + '">' + (isInclude ? '在区间' : '排除区间') + '</button>' +
                    '  <button class="btn btn-xs btn-danger nf-range-del"><i class="fa fa-times"></i></button>' +
                    '</div>';
                $('#nf-range-list', layero).append(html);
            },

            /**
             * 渲染筛号器号码网格
             */
            renderNumberFilterGrid: function (layero) {
                var colorMap = Controller.api.colorMap;
                var html = '';
                for (var num = 1; num <= 49; num++) {
                    var colorHex = Controller.api.getColorByNum(num);
                    var colorRaw = colorMap[num] || '';
                    var animal = Controller.api.getAnimalByNum(num);
                    var colorLabel = '';
                    if (colorRaw.indexOf('红') !== -1) colorLabel = '红';
                    else if (colorRaw.indexOf('蓝') !== -1) colorLabel = '蓝';
                    else if (colorRaw.indexOf('绿') !== -1) colorLabel = '绿';
                    html += '<div class="nf-number" data-num="' + num + '" data-color="' + colorLabel + '" data-animal="' + animal + '" data-tail="' + (num % 10) + '" data-parity="' + (num % 2 === 1 ? '单' : '双') + '" style="text-align:center;background:#f9f9f9;padding:6px 4px;border-radius:6px;min-width:60px;transition:opacity 0.2s;cursor:pointer;" title="点击屏蔽/恢复">' +
                        '<span style="display:inline-block;width:36px;height:36px;line-height:36px;text-align:center;border-radius:50%;color:#fff;background-color:' + colorHex + ';font-weight:bold;">' + num + '</span>' +
                        '<div style="font-size:10px;color:#666;line-height:1.2;">' + animal + '</div>' +
                        '</div>';
                }
                $('#nf-numbers', layero).html(html);
            },

            /**
             * 应用筛号器过滤条件
             * @param {object} layero Layer弹窗对象
             * @param {Array} blockedNums 手动屏蔽的号码列表
             */
            applyNumberFilters: function (layero, blockedNums) {
                blockedNums = blockedNums || [];
                // 收集所有选中的尾号
                var excludedTails = [];
                $('.nf-tail-select', layero).each(function () {
                    var val = $(this).val();
                    if (val !== '' && excludedTails.indexOf(parseInt(val)) === -1) {
                        excludedTails.push(parseInt(val));
                    }
                });
                // 收集被点击（置灰）的生肖
                var excludedZodiacs = [];
                $('.nf-zodiac.btn-gray', layero).each(function () {
                    excludedZodiacs.push($(this).data('zodiac'));
                });
                // 收集被点击（置灰）的波色
                var excludedColors = [];
                $('.nf-color-btn.btn-gray', layero).each(function () {
                    excludedColors.push($(this).data('color'));
                });
                // 收集被点击（置灰）的单双
                var excludedParities = [];
                $('.nf-parity.btn-gray', layero).each(function () {
                    excludedParities.push($(this).data('parity'));
                });
                // 收集所有区间
                var ranges = [];
                $('.nf-range-row', layero).each(function () {
                    var $row = $(this);
                    var min = parseInt($row.find('.nf-range-min').val()) || 1;
                    var max = parseInt($row.find('.nf-range-max').val()) || 49;
                    var mode = $row.find('.nf-range-mode').hasClass('btn-info') ? 'include' : 'exclude';
                    ranges.push({min: min, max: max, mode: mode});
                });

                $('.nf-number', layero).each(function () {
                    var $num = $(this);
                    var num = parseInt($num.data('num'));
                    var tail = $num.data('tail');
                    var animal = $num.data('animal');
                    var color = $num.data('color');

                    var parity = $num.data('parity');

                    var hidden = false;

                    // 尾号筛选：选中多个尾号，任一命中即屏蔽
                    if (excludedTails.length > 0 && excludedTails.indexOf(tail) !== -1) {
                        hidden = true;
                    }
                    // 单双筛选：选中单或双，匹配则屏蔽
                    if (excludedParities.indexOf(parity) !== -1) {
                        hidden = true;
                    }
                    // 区间筛选：在区间=白名单(OR)、排除区间=黑名单(OR)
                    if (!hidden) {
                        var includeRanges = [];
                        var excludeRanges = [];
                        for (var r = 0; r < ranges.length; r++) {
                            if (ranges[r].mode === 'include') {
                                includeRanges.push(ranges[r]);
                            } else {
                                excludeRanges.push(ranges[r]);
                            }
                        }
                        // 黑名单：任一排除区间命中则屏蔽
                        for (var r = 0; r < excludeRanges.length; r++) {
                            if (num >= excludeRanges[r].min && num <= excludeRanges[r].max) {
                                hidden = true;
                                break;
                            }
                        }
                        // 白名单：有在区间规则时，号码不在任一区间则屏蔽
                        if (!hidden && includeRanges.length > 0) {
                            var inAnyInclude = false;
                            for (var r = 0; r < includeRanges.length; r++) {
                                if (num >= includeRanges[r].min && num <= includeRanges[r].max) {
                                    inAnyInclude = true;
                                    break;
                                }
                            }
                            if (!inAnyInclude) {
                                hidden = true;
                            }
                        }
                    }
                    // 排除的生肖
                    if (excludedZodiacs.indexOf(animal) !== -1) {
                        hidden = true;
                    }
                    // 排除的波色
                    if (excludedColors.indexOf(color) !== -1) {
                        hidden = true;
                    }
                    // 手动屏蔽的号码
                    if (blockedNums.indexOf(num) !== -1) {
                        hidden = true;
                    }

                    if (hidden) {
                        $num.css('opacity', '0.25').css('filter', 'grayscale(100%)');
                    } else {
                        $num.css('opacity', '1').css('filter', 'none');
                    }
                });
            },

            bindevent: function () {
                Form.api.bindevent($("form[role=form]"));
            },

            /**
             * 通用分析弹窗（波色、生肖、奇偶、大小、尾数）
             */
            showAnalysisDialog: function (type) {
                var titles = {
                    colorWave: __('Color Wave'),
                    zodiac: __('Zodiac'),
                    oddEven: __('Odd/Even'),
                    bigSmall: __('Big/Small'),
                    tailNumbers: __('Tail Numbers')
                };
                var endpoints = {
                    colorWave: 'history/colorWaveAnalysis',
                    zodiac: 'history/zodiacAnalysis',
                    oddEven: 'history/oddEvenAnalysis',
                    bigSmall: 'history/bigSmallAnalysis',
                    tailNumbers: 'history/tailNumbers'
                };
                var hasType = ['colorWave', 'zodiac', 'oddEven', 'bigSmall', 'tailNumbers'].indexOf(type) !== -1;

                var html = '<div style="padding:20px;">';
                if (hasType) {
                    html += '<div class="form-group" style="border-bottom:1px solid #eee;padding-bottom:10px;margin-bottom:10px;">' +
                        '  <label style="margin-right:15px;">' + __('Query Type') + '：</label>' +
                        '  <label class="radio-inline" style="margin-right:15px;">' +
                        '    <input type="radio" name="analysis-type-' + type + '" value="all" checked> ' + __('All Numbers') +
                        '  </label>' +
                        '  <label class="radio-inline">' +
                        '    <input type="radio" name="analysis-type-' + type + '" value="special"> ' + __('Special Only') +
                        '  </label>' +
                        '</div>';
                }
                html += '<div class="form-group">' +
                    '  <label>' + __('Query Periods') + '：</label>' +
                    '  <input type="number" id="analysis-periods-' + type + '" class="form-control" value="30" min="10" max="100" style="width:120px;display:inline-block;">' +
                    '  <button class="btn btn-primary" id="btn-analysis-' + type + '" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
                    '</div>' +
                    '<div id="analysis-result-' + type + '" style="margin-top:15px;overflow-x:auto;"></div>' +
                    '</div>';

                Layer.open({
                    type: 1,
                    title: titles[type] || type,
                    area: ['650px', '550px'],
                    content: html,
                    shadeClose: true,
                    success: function (layero, index) {
                        $('#btn-analysis-' + type, layero).on('click', function () {
                            var periods = parseInt($('#analysis-periods-' + type, layero).val()) || 30;
                            var tp = hasType ? $('input[name="analysis-type-' + type + '"]:checked', layero).val() : 'all';
                            Controller.api.queryAnalysis(periods, tp, type, endpoints[type], layero);
                        });
                        if (hasType) {
                            $('input[name="analysis-type-' + type + '"]', layero).on('change', function () {
                                var periods = parseInt($('#analysis-periods-' + type, layero).val()) || 30;
                                Controller.api.queryAnalysis(periods, $(this).val(), type, endpoints[type], layero);
                            });
                        }
                    }
                });
            },

            queryAnalysis: function (periods, type, analysisType, endpoint, layero) {
                var $btn = $('#btn-analysis-' + analysisType, layero);
                $btn.prop('disabled', true);
                $('#analysis-result-' + analysisType, layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
                $.ajax({
                    url: endpoint,
                    type: 'GET',
                    data: {periods: periods, type: type},
                    dataType: 'json',
                    success: function (ret) {
                        if (ret.code == 1) {
                            var renderMap = {
                                colorWave: 'renderColorWaveAnalysis',
                                zodiac: 'renderZodiacAnalysis',
                                oddEven: 'renderOddEvenAnalysis',
                                bigSmall: 'renderBigSmallAnalysis',
                                tailNumbers: 'renderTailNumbers'
                            };
                            var renderFn = renderMap[analysisType];
                            if (renderFn && typeof Controller.api[renderFn] === 'function') {
                                Controller.api[renderFn](ret.data, layero);
                            } else {
                                $('#analysis-result-' + analysisType, layero).html('<div class="alert alert-danger">渲染方法不存在: ' + renderFn + '</div>');
                            }
                        } else {
                            $('#analysis-result-' + analysisType, layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
                        }
                    },
                    error: function () {
                        $('#analysis-result-' + analysisType, layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
                    },
                    complete: function () {
                        $btn.prop('disabled', false);
                    }
                });
            },

            renderColorWaveAnalysis: function (data, layero) {
                var html = '<div style="padding:15px;"><div style="display:flex;justify-content:space-around;">';
                var items = [
                    {label: '红波', value: data.red, pct: data.red_pct, color: '#e74c3c'},
                    {label: '蓝波', value: data.blue, pct: data.blue_pct, color: '#3498db'},
                    {label: '绿波', value: data.green, pct: data.green_pct, color: '#2ecc71'}
                ];
                for (var i = 0; i < items.length; i++) {
                    var item = items[i];
                    html += '<div style="text-align:center;padding:20px;border-radius:8px;background:#f5f5f5;min-width:120px;">' +
                        '<div style="width:60px;height:60px;line-height:60px;border-radius:50%;background-color:' + item.color + ';color:#fff;font-size:24px;font-weight:bold;margin:0 auto;">' + item.value + '</div>' +
                        '<div style="margin-top:10px;font-size:14px;color:#333;">' + item.label + '</div>' +
                        '<div style="font-size:12px;color:#999;">' + item.pct + '%</div>' +
                        '</div>';
                }
                html += '</div><div style="margin-top:15px;color:#999;font-size:12px;text-align:center;">总计: ' + data.total + ' 个号码</div></div>';
                $('#analysis-result-colorWave', layero).html(html);
            },

            renderZodiacAnalysis: function (data, layero) {
                var html = '<div style="padding:15px;"><div style="display:flex;flex-wrap:wrap;gap:8px;">';
                for (var i = 0; i < data.list.length; i++) {
                    var item = data.list[i];
                    html += '<div style="text-align:center;background:#f9f9f9;padding:10px 15px;border-radius:6px;min-width:90px;">' +
                        '<div style="font-size:18px;font-weight:bold;color:#333;">' + item.animal + '</div>' +
                        '<div style="font-size:12px;color:#666;">' + item.count + ' (' + item.percent + '%)</div>' +
                        '</div>';
                }
                html += '</div></div>';
                $('#analysis-result-zodiac', layero).html(html);
            },

            renderOddEvenAnalysis: function (data, layero) {
                var html = '<div style="padding:15px;"><div style="display:flex;justify-content:space-around;margin-bottom:15px;">';
                html += '<div style="text-align:center;padding:15px;border-radius:8px;background:#f5f5f5;min-width:140px;">' +
                    '<div style="font-size:28px;font-weight:bold;color:#e74c3c;">' + data.odd + '</div>' +
                    '<div style="font-size:14px;color:#333;">奇数</div>' +
                    '<div style="font-size:12px;color:#999;">' + data.odd_pct + '%</div></div>';
                html += '<div style="text-align:center;padding:15px;border-radius:8px;background:#f5f5f5;min-width:140px;">' +
                    '<div style="font-size:28px;font-weight:bold;color:#3498db;">' + data.even + '</div>' +
                    '<div style="font-size:14px;color:#333;">偶数</div>' +
                    '<div style="font-size:12px;color:#999;">' + data.even_pct + '%</div></div>';
                html += '</div></div>';
                $('#analysis-result-oddEven', layero).html(html);
            },

            renderBigSmallAnalysis: function (data, layero) {
                var html = '<div style="padding:15px;"><div style="display:flex;justify-content:space-around;margin-bottom:15px;">';
                html += '<div style="text-align:center;padding:15px;border-radius:8px;background:#f5f5f5;min-width:140px;">' +
                    '<div style="font-size:28px;font-weight:bold;color:#f39c12;">' + data.big + '</div>' +
                    '<div style="font-size:14px;color:#333;">大数(25-49)</div>' +
                    '<div style="font-size:12px;color:#999;">' + data.big_pct + '%</div></div>';
                html += '<div style="text-align:center;padding:15px;border-radius:8px;background:#f5f5f5;min-width:140px;">' +
                    '<div style="font-size:28px;font-weight:bold;color:#2ecc71;">' + data.small + '</div>' +
                    '<div style="font-size:14px;color:#333;">小数(1-24)</div>' +
                    '<div style="font-size:12px;color:#999;">' + data.small_pct + '%</div></div>';
                html += '</div></div>';
                $('#analysis-result-bigSmall', layero).html(html);
            },

            renderTailNumbers: function (data, layero) {
                var html = '<div style="padding:15px;"><div style="display:flex;flex-wrap:wrap;gap:8px;">';
                for (var i = 0; i < data.all.length; i++) {
                    var item = data.all[i];
                    html += '<div style="text-align:center;background:#f9f9f9;padding:10px 15px;border-radius:6px;min-width:70px;">' +
                        '<div style="font-size:22px;font-weight:bold;color:#333;">' + item.tail + '</div>' +
                        '<div style="font-size:12px;color:#666;">' + item.count + ' (' + item.percent + '%)</div></div>';
                }
                html += '</div></div>';
                $('#analysis-result-tailNumbers', layero).html(html);
            },

            /**
             * 和值分析弹窗
             */
            showSumDialog: function () {
                var html = '<div style="padding:20px;">' +
                    '<div class="form-group">' +
                    '  <label>' + __('Query Periods') + '：</label>' +
                    '  <input type="number" id="sum-periods" class="form-control" value="30" min="10" max="100" style="width:120px;display:inline-block;">' +
                    '  <button class="btn btn-primary" id="btn-sum-query" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
                    '</div>' +
                    '<div id="sum-result" style="margin-top:15px;"></div>' +
                    '</div>';

                Layer.open({
                    type: 1,
                    title: __('Sum Chart'),
                    area: ['750px', '400px'],
                    content: html,
                    shadeClose: true,
                    success: function (layero, index) {
                        $('#btn-sum-query', layero).on('click', function () {
                            var periods = parseInt($('#sum-periods', layero).val()) || 30;
                            Controller.api.querySum(periods, layero);
                        });
                    }
                });
            },

            querySum: function (periods, layero) {
                var $btn = $('#btn-sum-query', layero);
                $btn.prop('disabled', true);
                $('#sum-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
                $.ajax({
                    url: 'history/sumAnalysis',
                    type: 'GET',
                    data: {periods: periods},
                    dataType: 'json',
                    success: function (ret) {
                        if (ret.code == 1) {
                            Controller.api.renderSum(ret.data, layero);
                        } else {
                            $('#sum-result', layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
                        }
                    },
                    error: function () {
                        $('#sum-result', layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
                    },
                    complete: function () {
                        $btn.prop('disabled', false);
                    }
                });
            },

            renderSum: function (data, layero) {
                var html = '<div style="padding:15px;"><div style="display:flex;justify-content:space-around;margin-bottom:15px;">';
                html += '<div style="text-align:center;padding:10px;"><div style="font-size:20px;font-weight:bold;color:#333;">' + data.avg + '</div><div style="font-size:12px;color:#999;">平均和值</div></div>';
                html += '<div style="text-align:center;padding:10px;"><div style="font-size:20px;font-weight:bold;color:#e74c3c;">' + data.max + '</div><div style="font-size:12px;color:#999;">最大和值</div></div>';
                html += '<div style="text-align:center;padding:10px;"><div style="font-size:20px;font-weight:bold;color:#3498db;">' + data.min + '</div><div style="font-size:12px;color:#999;">最小和值</div></div>';
                html += '</div><div id="sum-chart" style="width:100%;height:250px;"></div></div>';
                $('#sum-result', layero).html(html);

                var chartDom = document.getElementById('sum-chart');
                if (chartDom && typeof echarts !== 'undefined') {
                    var myChart = echarts.init(chartDom);
                    myChart.setOption({
                        xAxis: {type: 'category', data: data.expects, axisLabel: {rotate: 45, fontSize: 10}},
                        yAxis: {type: 'value'},
                        series: [{type: 'line', data: data.sums, smooth: true, itemStyle: {color: '#3498db'}, areaStyle: {color: 'rgba(52,152,219,0.1)'}}],
                        grid: {left: 40, right: 20, bottom: 40, top: 20}
                    });
                }
            },

            /**
             * 连号分析弹窗
             */
            showConsecutiveDialog: function () {
                var html = '<div style="padding:20px;">' +
                    '<div class="form-group">' +
                    '  <label>' + __('Query Periods') + '：</label>' +
                    '  <input type="number" id="consecutive-periods" class="form-control" value="30" min="10" max="100" style="width:120px;display:inline-block;">' +
                    '  <button class="btn btn-primary" id="btn-consecutive-query" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
                    '</div>' +
                    '<div id="consecutive-result" style="margin-top:15px;overflow-x:auto;"></div>' +
                    '</div>';

                Layer.open({
                    type: 1,
                    title: __('Consecutive'),
                    area: ['600px', '500px'],
                    content: html,
                    shadeClose: true,
                    success: function (layero, index) {
                        $('#btn-consecutive-query', layero).on('click', function () {
                            var periods = parseInt($('#consecutive-periods', layero).val()) || 30;
                            Controller.api.queryConsecutive(periods, layero);
                        });
                    }
                });
            },

            queryConsecutive: function (periods, layero) {
                var $btn = $('#btn-consecutive-query', layero);
                $btn.prop('disabled', true);
                $('#consecutive-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
                $.ajax({
                    url: 'history/consecutiveNumbers',
                    type: 'GET',
                    data: {periods: periods},
                    dataType: 'json',
                    success: function (ret) {
                        if (ret.code == 1) {
                            Controller.api.renderConsecutive(ret.data, layero);
                        } else {
                            $('#consecutive-result', layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
                        }
                    },
                    error: function () {
                        $('#consecutive-result', layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
                    },
                    complete: function () {
                        $btn.prop('disabled', false);
                    }
                });
            },

            renderConsecutive: function (data, layero) {
                var html = '<div style="padding:15px;">';
                html += '<h4 style="margin:0 0 10px 0;border-bottom:1px solid #eee;padding-bottom:5px;">连号对</h4>';
                html += '<div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:15px;">';
                var pairs = data.pairs;
                if (pairs && Object.keys(pairs).length > 0) {
                    for (var pair in pairs) {
                        html += '<div style="background:#f5f5f5;padding:6px 12px;border-radius:4px;font-size:13px;">' + pair + ' <b>×' + pairs[pair] + '</b></div>';
                    }
                } else {
                    html += '<div style="color:#999;font-size:13px;">暂无连号数据</div>';
                }
                html += '</div><h4 style="margin:0 0 10px 0;border-bottom:1px solid #eee;padding-bottom:5px;">三连号</h4>';
                html += '<div style="display:flex;flex-wrap:wrap;gap:6px;">';
                var triples = data.triples;
                if (triples && Object.keys(triples).length > 0) {
                    for (var triple in triples) {
                        html += '<div style="background:#f5f5f5;padding:6px 12px;border-radius:4px;font-size:13px;">' + triple + ' <b>×' + triples[triple] + '</b></div>';
                    }
                } else {
                    html += '<div style="color:#999;font-size:13px;">暂无三连号数据</div>';
                }
                html += '</div></div>';
                $('#consecutive-result', layero).html(html);
            },

            /**
             * 综合统计面板
             */
            showDashboard: function () {
                var html = '<div style="padding:20px;">' +
                    '<div class="form-group">' +
                    '  <label>' + __('Query Periods') + '：</label>' +
                    '  <input type="number" id="dash-periods" class="form-control" value="30" min="10" max="100" style="width:120px;display:inline-block;">' +
                    '  <button class="btn btn-primary" id="btn-dash-query" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
                    '</div>' +
                    '<div id="dash-result" style="margin-top:15px;"></div>' +
                    '</div>';

                Layer.open({
                    type: 1,
                    title: __('Dashboard'),
                    area: ['90%', '90%'],
                    content: html,
                    shadeClose: false,
                    maxmin: true,
                    success: function (layero, index) {
                        $('#btn-dash-query', layero).on('click', function () {
                            var periods = parseInt($('#dash-periods', layero).val()) || 30;
                            Controller.api.queryDashboard(periods, layero);
                        });
                    }
                });
            },

            queryDashboard: function (periods, layero) {
                var $btn = $('#btn-dash-query', layero);
                $btn.prop('disabled', true);
                $('#dash-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
                $.ajax({
                    url: 'history/dashboard',
                    type: 'GET',
                    data: {periods: periods},
                    dataType: 'json',
                    success: function (ret) {
                        if (ret.code == 1) {
                            Controller.api.renderDashboard(ret.data, layero);
                        } else {
                            $('#dash-result', layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
                        }
                    },
                    error: function () {
                        $('#dash-result', layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
                    },
                    complete: function () {
                        $btn.prop('disabled', false);
                    }
                });
            },

            renderDashboard: function (data, layero) {
                var getColor = function (color) {
                    if (!color) return '#95a5a6';
                    if (color.indexOf('红') !== -1) return '#e74c3c';
                    if (color.indexOf('蓝') !== -1) return '#3498db';
                    if (color.indexOf('绿') !== -1) return '#2ecc71';
                    return '#95a5a6';
                };

                var hc = data.hotcold;
                var cw = data.colorwave;
                var zo = data.zodiac;
                var oe = data.oddeven;
                var bs = data.bigsmall;
                var sm = data.sum;
                var tn = data.tailnumbers;

                var ballHtml = function (item) {
                    return '<div style="text-align:center;display:inline-block;margin:3px;"><span style="display:inline-block;width:28px;height:28px;line-height:28px;text-align:center;border-radius:50%;color:#fff;background-color:' + getColor(item.color) + ';font-weight:bold;font-size:12px;">' + item.num + '</span><div style="font-size:9px;color:#666;">' + item.count + '</div></div>';
                };

                var html = '<div style="padding:10px;max-height:75vh;overflow-y:auto;">';

                // 冷热号码
                html += '<h4 style="border-bottom:1px solid #eee;padding-bottom:5px;">🔥❄️ 冷热号码</h4>';
                html += '<div style="display:flex;"><div style="flex:1;padding:5px;"><b style="color:#e74c3c;">热号 Top5</b><div>';
                for (var i = 0; i < 5; i++) html += ballHtml(hc.hot[i]);
                html += '</div></div><div style="flex:1;padding:5px;"><b style="color:#3498db;">冷号 Top5</b><div>';
                for (var i = 0; i < 5; i++) html += ballHtml(hc.cold[i]);
                html += '</div></div></div>';

                // 波色分析
                html += '<h4 style="border-bottom:1px solid #eee;padding-bottom:5px;">🎨 波色比例</h4>';
                html += '<div style="display:flex;gap:15px;">';
                html += '<div style="flex:1;text-align:center;padding:8px;background:#fce4ec;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#e74c3c;">' + cw.red + '</div><div>红波 ' + cw.red_pct + '%</div></div>';
                html += '<div style="flex:1;text-align:center;padding:8px;background:#e3f2fd;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#3498db;">' + cw.blue + '</div><div>蓝波 ' + cw.blue_pct + '%</div></div>';
                html += '<div style="flex:1;text-align:center;padding:8px;background:#e8f5e9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#2ecc71;">' + cw.green + '</div><div>绿波 ' + cw.green_pct + '%</div></div>';
                html += '</div>';

                // 生肖分析
                html += '<h4 style="border-bottom:1px solid #eee;padding-bottom:5px;">⭐ 生肖排名</h4>';
                html += '<div style="display:flex;flex-wrap:wrap;gap:5px;">';
                for (var i = 0; i < Math.min(zo.list.length, 12); i++) {
                    var z = zo.list[i];
                    html += '<div style="text-align:center;background:#f5f5f5;padding:5px 10px;border-radius:4px;"><div style="font-size:14px;font-weight:bold;">' + z.animal + '</div><div style="font-size:10px;color:#666;">' + z.count + ' (' + z.percent + '%)</div></div>';
                }
                html += '</div>';

                // 奇偶分析
                html += '<h4 style="border-bottom:1px solid #eee;padding-bottom:5px;">⚖️ 奇偶分析</h4>';
                html += '<div style="display:flex;gap:15px;">';
                html += '<div style="flex:1;text-align:center;padding:8px;background:#f9f9f9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#e74c3c;">' + oe.odd + ' (' + oe.odd_pct + '%)</div><div>奇数</div></div>';
                html += '<div style="flex:1;text-align:center;padding:8px;background:#f9f9f9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#3498db;">' + oe.even + ' (' + oe.even_pct + '%)</div><div>偶数</div></div>';
                html += '</div>';

                // 大小分析
                html += '<h4 style="border-bottom:1px solid #eee;padding-bottom:5px;">📊 大小分析</h4>';
                html += '<div style="display:flex;gap:15px;">';
                html += '<div style="flex:1;text-align:center;padding:8px;background:#f9f9f9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#f39c12;">' + bs.big + ' (' + bs.big_pct + '%)</div><div>大数(25-49)</div></div>';
                html += '<div style="flex:1;text-align:center;padding:8px;background:#f9f9f9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#2ecc71;">' + bs.small + ' (' + bs.small_pct + '%)</div><div>小数(1-24)</div></div>';
                html += '</div>';

                // 和值
                html += '<h4 style="border-bottom:1px solid #eee;padding-bottom:5px;">📈 和值统计</h4>';
                html += '<div style="display:flex;gap:15px;">';
                html += '<div style="flex:1;text-align:center;padding:8px;background:#f9f9f9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;">' + sm.avg + '</div><div>平均</div></div>';
                html += '<div style="flex:1;text-align:center;padding:8px;background:#f9f9f9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#e74c3c;">' + sm.max + '</div><div>最大</div></div>';
                html += '<div style="flex:1;text-align:center;padding:8px;background:#f9f9f9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#3498db;">' + sm.min + '</div><div>最小</div></div>';
                html += '</div>';

                // 尾数
                html += '<h4 style="border-bottom:1px solid #eee;padding-bottom:5px;">🔢 尾数频率</h4>';
                html += '<div style="display:flex;flex-wrap:wrap;gap:5px;">';
                for (var i = 0; i < tn.all.length; i++) {
                    var t = tn.all[i];
                    html += '<div style="text-align:center;background:#f5f5f5;padding:5px 10px;border-radius:4px;"><div style="font-size:16px;font-weight:bold;">' + t.tail + '</div><div style="font-size:10px;color:#666;">' + t.count + ' (' + t.percent + '%)</div></div>';
                }
                html += '</div>';

                html += '</div>';
                $('#dash-result', layero).html(html);
            },

            /**
             * 预测号码弹窗
             */
            showPredictDialog: function () {
                var html = '<div style="padding:20px;">' +
                    '<div style="margin-bottom:15px;background:#fff3cd;border:1px solid #ffc107;padding:10px;border-radius:6px;">' +
                    '  <div style="font-size:13px;font-weight:bold;color:#856404;margin-bottom:8px;"><i class="fa fa-lightbulb-o"></i> 预测算法说明</div>' +
                    '  <div style="font-size:12px;color:#666;">' +
                    '    <b>V1版本</b>：基于转移概率分析（区域、生肖、尾号、首号、波色转移 + 冷热系数）<br>' +
                    '    <b>V2版本</b>：基于统计回归分析（遗漏回归、频率回归、区域平衡、波色平衡等）+ 历史回测验证<br>' +
                    '    <b>V3版本（推荐）</b>：多维度综合预测，新增转移概率（马尔可夫链）、单双规律、大小规律、走势方向分析' +
                    '  </div>' +
                    '</div>' +
                    '<div class="form-group" style="border-bottom:1px solid #eee;padding-bottom:10px;margin-bottom:10px;">' +
                    '  <label style="margin-right:10px;">算法版本：</label>' +
                    '  <label class="radio-inline" style="margin-right:15px;"><input type="radio" name="predict-version" value="v3" checked> <b>V3</b>（多维度综合）</label>' +
                    '  <label class="radio-inline" style="margin-right:15px;"><input type="radio" name="predict-version" value="v2"> V2（统计回归）</label>' +
                    '  <label class="radio-inline"><input type="radio" name="predict-version" value="v1"> V1（转移概率）</label>' +
                    '</div>' +
                    '<div class="form-group" style="border-bottom:1px solid #eee;padding-bottom:10px;margin-bottom:10px;">' +
                    '  <label style="margin-right:10px;">验证期号：</label>' +
                    '  <input type="text" id="predict-target" class="form-control" placeholder="输入期号验证历史预测（可选）" style="width:180px;display:inline-block;">' +
                    '  <span style="font-size:11px;color:#999;margin-left:5px;">留空则预测下一期</span>' +
                    '</div>' +
                    '<div class="form-group" style="border-bottom:1px solid #eee;padding-bottom:10px;margin-bottom:10px;">' +
                    '  <label style="margin-right:10px;">统计期数：</label>' +
                    '  <input type="number" id="predict-periods" class="form-control" value="200" min="30" max="500" style="width:120px;display:inline-block;">' +
                    '  <span style="font-size:11px;color:#999;margin-left:5px;">建议200期以上</span>' +
                    '</div>' +
                    // V3权重配置
                    '<div id="predict-v3-weights" style="border-bottom:1px solid #eee;padding-bottom:10px;margin-bottom:10px;">' +
                    '  <label style="margin-right:10px;">V3权重配置：</label>' +
                    '  <div style="display:flex;flex-wrap:wrap;gap:10px;font-size:12px;">' +
                    '    <div>遗漏回归: <input type="number" class="predict-weight-v3" data-key="omit_regression" value="0.20" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>频率回归: <input type="number" class="predict-weight-v3" data-key="freq_regression" value="0.15" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>转移概率: <input type="number" class="predict-weight-v3" data-key="transition_prob" value="0.20" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>单双平衡: <input type="number" class="predict-weight-v3" data-key="oddeven_balance" value="0.10" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>大小平衡: <input type="number" class="predict-weight-v3" data-key="bigsmall_balance" value="0.10" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>走势方向: <input type="number" class="predict-weight-v3" data-key="trend_direction" value="0.15" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>区域平衡: <input type="number" class="predict-weight-v3" data-key="zone_balance" value="0.05" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>波色平衡: <input type="number" class="predict-weight-v3" data-key="color_balance" value="0.05" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '  </div>' +
                    '</div>' +
                    // V2权重配置
                    '<div id="predict-v2-weights" style="display:none;border-bottom:1px solid #eee;padding-bottom:10px;margin-bottom:10px;">' +
                    '  <label style="margin-right:10px;">V2权重配置：</label>' +
                    '  <div style="display:flex;flex-wrap:wrap;gap:10px;font-size:12px;">' +
                    '    <div>遗漏回归: <input type="number" class="predict-weight-v2" data-key="omit_regression" value="0.30" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>频率回归: <input type="number" class="predict-weight-v2" data-key="freq_regression" value="0.25" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>近期趋势: <input type="number" class="predict-weight-v2" data-key="recent_trend" value="0.20" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>区域平衡: <input type="number" class="predict-weight-v2" data-key="zone_balance" value="0.10" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>波色平衡: <input type="number" class="predict-weight-v2" data-key="color_balance" value="0.08" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>统计显著性: <input type="number" class="predict-weight-v2" data-key="stat_significance" value="0.07" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '  </div>' +
                    '</div>' +
                    // V1权重配置
                    '<div id="predict-v1-weights" style="display:none;border-bottom:1px solid #eee;padding-bottom:10px;margin-bottom:10px;">' +
                    '  <label style="margin-right:10px;">V1权重配置：</label>' +
                    '  <div style="display:flex;flex-wrap:wrap;gap:10px;font-size:12px;">' +
                    '    <div>区域: <input type="number" class="predict-weight" data-key="zone" value="0.25" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>生肖: <input type="number" class="predict-weight" data-key="zodiac" value="0.20" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>尾号: <input type="number" class="predict-weight" data-key="tail" value="0.20" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>首号: <input type="number" class="predict-weight" data-key="head" value="0.15" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>波色: <input type="number" class="predict-weight" data-key="color" value="0.10" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '    <div>冷热: <input type="number" class="predict-weight" data-key="hotcold" value="0.10" min="0" max="1" step="0.05" style="width:60px;"></div>' +
                    '  </div>' +
                    '</div>' +
                    '<div style="text-align:center;margin-bottom:15px;">' +
                    '  <button class="btn btn-primary" id="btn-predict-query"><i class="fa fa-magic"></i> 开始预测</button>' +
                    '</div>' +
                    '<div id="predict-result" style="margin-top:15px;"></div>' +
                    '</div>';

                Layer.open({
                    type: 1,
                    title: '🎯 智能预测号码',
                    area: ['850px', '750px'],
                    content: html,
                    shadeClose: true,
                    success: function (layero, index) {
                        // 切换版本时显示对应权重配置
                        $('input[name="predict-version"]', layero).on('change', function () {
                            var val = $(this).val();
                            $('#predict-v3-weights', layero).hide();
                            $('#predict-v2-weights', layero).hide();
                            $('#predict-v1-weights', layero).hide();
                            if (val === 'v3') {
                                $('#predict-v3-weights', layero).show();
                                $('#predict-periods', layero).val(200);
                            } else if (val === 'v2') {
                                $('#predict-v2-weights', layero).show();
                                $('#predict-periods', layero).val(200);
                            } else {
                                $('#predict-v1-weights', layero).show();
                                $('#predict-periods', layero).val(100);
                            }
                        });
                        $('#btn-predict-query', layero).on('click', function () {
                            Controller.api.queryPredict(layero);
                        });
                    }
                });
            },

            /**
             * 查询预测结果
             */
            queryPredict: function (layero) {
                var $btn = $('#btn-predict-query', layero);
                $btn.prop('disabled', true);
                $('#predict-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> 正在分析历史数据...</div>');

                var version = $('input[name="predict-version"]:checked', layero).val();
                var periods = parseInt($('#predict-periods', layero).val()) || 200;
                var targetExpect = $('#predict-target', layero).val().trim();
                var weights = {};

                // 根据版本获取权重
                if (version === 'v3') {
                    $('.predict-weight-v3', layero).each(function () {
                        var key = $(this).data('key');
                        var val = parseFloat($(this).val()) || 0;
                        weights[key] = val;
                    });
                } else if (version === 'v2') {
                    $('.predict-weight-v2', layero).each(function () {
                        var key = $(this).data('key');
                        var val = parseFloat($(this).val()) || 0;
                        weights[key] = val;
                    });
                } else {
                    $('.predict-weight', layero).each(function () {
                        var key = $(this).data('key');
                        var val = parseFloat($(this).val()) || 0;
                        weights[key] = val;
                    });
                }

                // 根据版本选择URL
                var url = 'history/predict';
                if (version === 'v3') {
                    url = 'history/predictV3';
                } else if (version === 'v2') {
                    url = 'history/predictV2';
                }

                $.ajax({
                    url: url,
                    type: 'GET',
                    data: { periods: periods, weights: JSON.stringify(weights), target_expect: targetExpect },
                    dataType: 'json',
                    success: function (ret) {
                        if (ret.code == 1) {
                            Controller.api.renderPredict(ret.data, layero, version);
                        } else {
                            $('#predict-result', layero).html('<div class="alert alert-danger">' + (ret.msg || '预测失败') + '</div>');
                        }
                    },
                    error: function () {
                        $('#predict-result', layero).html('<div class="alert alert-danger">预测请求失败</div>');
                    },
                    complete: function () {
                        $btn.prop('disabled', false);
                    }
                });
            },

            /**
             * 渲染预测结果
             */
            renderPredict: function (data, layero, version) {
                var predictions = data.predictions || [];
                var analysis = data.analysis || {};
                var hitInfo = data.hit_info || null;
                var actualResult = data.actual_result || null;
                var backtest = data.backtest || null;
                var confidence = data.confidence || null;

                if (predictions.length === 0) {
                    $('#predict-result', layero).html('<div class="alert alert-info">暂无预测结果</div>');
                    return;
                }

                // 上期特码信息
                var lastSpecial = analysis.last_special || 0;
                var lastExpect = analysis.last_expect || '';
                var lastColor = Controller.api.getColorByNum(lastSpecial);
                var lastAnimal = Controller.api.getAnimalByNum(lastSpecial);

                // 版本名称映射
                var versionNames = {
                    'v1': 'V1（转移概率）',
                    'v2': 'V2（统计回归）',
                    'v3': 'V3（多维度综合）'
                };

                var html = '<div style="padding:10px;">';

                // 基准期号标题
                html += '<div style="font-size:12px;color:#666;margin-bottom:10px;">基于期号 <b>' + lastExpect + '</b>（特码 ' + lastSpecial + '）进行预测 | 算法版本: <b>' + versionNames[version] + '</b></div>';

                // 置信度评估展示（V2和V3版本）
                if (confidence && (version === 'v2' || version === 'v3')) {
                    html += '<div style="background:#fff8e1;border:1px solid #ffb300;border-radius:6px;padding:12px;margin-bottom:15px;">';
                    html += '<div style="font-size:13px;font-weight:bold;color:#ff8f00;margin-bottom:8px;"><i class="fa fa-star-half-o"></i> 预测置信度评估</div>';

                    // 数据警告提示（数据不足时显示）
                    if (confidence.data_warning) {
                        html += '<div style="font-size:11px;color:#d32f2f;background:#ffebee;padding:6px;border-radius:4px;margin-bottom:8px;"><i class="fa fa-exclamation-triangle"></i> ' + confidence.data_warning + '</div>';
                    }

                    html += '<div style="display:flex;gap:20px;align-items:center;">';
                    html += '<div style="text-align:center;"><div style="font-size:24px;font-weight:bold;color:#ff8f00;">' + confidence.overall_confidence + '%</div><div style="font-size:12px;color:#666;">整体置信度</div></div>';

                    // 各排名置信度（使用得分集中度维度）
                    if (confidence.confidence_scores && confidence.confidence_scores.length > 0) {
                        html += '<div style="display:flex;gap:8px;">';
                        for (var i = 0; i < confidence.confidence_scores.length; i++) {
                            var cs = confidence.confidence_scores[i];
                            // 阈值定义：>=70%高(绿)、50-70%中(橙)、<50%低(红)
                            var confLevel = cs.confidence >= 70 ? '高' : (cs.confidence >= 50 ? '中' : '低');
                            var confColor = cs.confidence >= 70 ? '#4caf50' : (cs.confidence >= 50 ? '#ff9800' : '#f44336');
                            html += '<div style="text-align:center;padding:5px;background:#fff;border-radius:4px;">';
                            html += '<div style="font-size:14px;font-weight:bold;color:' + confColor + ';">' + cs.confidence + '%</div>';
                            html += '<div style="font-size:10px;color:#999;">#' + cs.rank + '</div>';
                            html += '</div>';
                        }
                        html += '</div>';
                    }
                    html += '</div></div>';
                }

                // 回测验证结果（V2和V3版本）
                if (backtest && (version === 'v2' || version === 'v3')) {
                    html += '<div style="background:#e3f2fd;border:1px solid #2196f3;border-radius:6px;padding:12px;margin-bottom:15px;">';
                    html += '<div style="font-size:13px;font-weight:bold;color:#1565c0;margin-bottom:8px;"><i class="fa fa-chart-line"></i> 历史回测验证（最近' + backtest.total_tests + '期）</div>';

                    // 回测数据警告提示
                    if (backtest.data_warning) {
                        html += '<div style="font-size:11px;color:#d32f2f;background:#ffebee;padding:6px;border-radius:4px;margin-bottom:8px;"><i class="fa fa-exclamation-triangle"></i> ' + backtest.data_warning + '</div>';
                    }

                    html += '<div style="display:flex;gap:15px;flex-wrap:wrap;">';
                    html += '<div style="text-align:center;padding:8px;"><div style="font-size:22px;font-weight:bold;color:#2196f3;">' + backtest.hit_rate + '%</div><div style="font-size:11px;color:#666;">命中率（Top5）</div></div>';
                    html += '<div style="text-align:center;padding:8px;"><div style="font-size:22px;font-weight:bold;color:#4caf50;">' + backtest.total_hits + '/' + backtest.total_tests + '</div><div style="font-size:11px;color:#666;">命中次数</div></div>';
                    html += '<div style="text-align:center;padding:8px;"><div style="font-size:22px;font-weight:bold;color:#ff9800;">' + (backtest.avg_rank || '—') + '</div><div style="font-size:11px;color:#666;">平均排名</div></div>';

                    // 新增指标：NDCG@5 和 MRR（百分比展示）
                    if (backtest.ndcg_5 !== undefined) {
                        html += '<div style="text-align:center;padding:8px;"><div style="font-size:22px;font-weight:bold;color:#9c27b0;">' + (backtest.ndcg_5 * 100).toFixed(1) + '%</div><div style="font-size:11px;color:#666;">NDCG@5</div></div>';
                    }
                    if (backtest.mrr !== undefined) {
                        html += '<div style="text-align:center;padding:8px;"><div style="font-size:22px;font-weight:bold;color:#00bcd4;">' + (backtest.mrr * 100).toFixed(1) + '%</div><div style="font-size:11px;color:#666;">MRR</div></div>';
                    }

                    // 转移概率阶数显示（来自analysis.transition_order字段）
                    if (analysis && analysis.transition_order !== undefined) {
                        html += '<div style="text-align:center;padding:8px;"><div style="font-size:22px;font-weight:bold;color:#607d8b;">' + analysis.transition_order + '阶</div><div style="font-size:11px;color:#666;">转移概率</div></div>';
                    }
                    html += '</div>';

                    // 命中分布柱状图（使用rank_1..rank_5键名）
                    if (backtest.hit_distribution && Object.keys(backtest.hit_distribution).length > 0) {
                        var distribution = backtest.hit_distribution;
                        var maxHit = 0;
                        // 找最大值用于计算柱状图高度比例
                        for (var r = 1; r <= 5; r++) {
                            var key = 'rank_' + r;
                            if (distribution[key] > maxHit) {
                                maxHit = distribution[key];
                            }
                        }

                        html += '<div style="margin-top:10px;font-size:11px;color:#666;">命中分布（各排名命中次数）：</div>';
                        html += '<div style="display:flex;gap:8px;align-items:flex-end;height:60px;margin-top:5px;padding:5px;background:#f5f5f5;border-radius:4px;">';
                        for (var r = 1; r <= 5; r++) {
                            var key = 'rank_' + r;
                            var hitCount = distribution[key] || 0;
                            var barHeight = maxHit > 0 ? (hitCount / maxHit * 45) : 0;
                            var barColor = hitCount > 0 ? '#4caf50' : '#e0e0e0';
                            html += '<div style="text-align:center;min-width:50px;">';
                            html += '<div style="height:' + barHeight + 'px;background:' + barColor + ';border-radius:2px 2px 0 0;width:35px;margin:0 auto;"></div>';
                            html += '<div style="font-size:10px;color:#666;margin-top:2px;">#' + r + '</div>';
                            html += '<div style="font-size:11px;color:#333;font-weight:bold;">' + hitCount + '</div>';
                            html += '</div>';
                        }
                        html += '</div>';
                    }

                    // 回测详情
                    if (backtest.details && backtest.details.length > 0) {
                        html += '<div style="margin-top:10px;font-size:11px;color:#666;">最近' + backtest.details.length + '期验证详情：</div>';
                        html += '<div style="margin-top:5px;max-height:300px;overflow-y:auto;">';
                        for (var i = 0; i < backtest.details.length; i++) {
                            var bd = backtest.details[i];
                            var hitTag = bd.hit ? '<span style="color:#4caf50;">✓</span>' : '<span style="color:#f44336;">✗</span>';
                            html += '<div style="font-size:11px;padding:3px 0;border-bottom:1px dashed #eee;">期号' + bd.expect + ': 实际<b>' + bd.actual + '</b> ' + hitTag + ' 预测[' + bd.predictions.join(',') + ']</div>';
                        }
                        html += '</div>';
                    }
                    html += '</div>';
                }

                // V3版本特有的分析信息
                if (version === 'v3' && analysis) {
                    html += '<div style="background:#f3e5f5;border:1px solid #9c27b0;border-radius:6px;padding:10px;margin-bottom:15px;font-size:12px;">';
                    html += '<div style="font-weight:bold;color:#7b1fa2;margin-bottom:8px;"><i class="fa fa-chart-bar"></i> V3多维度分析</div>';

                    // 单双统计
                    if (analysis.oddeven_stats) {
                        var oe = analysis.oddeven_stats;
                        html += '<div style="margin-bottom:5px;"><b>单双规律：</b> 单号' + oe.odd_pct + '% / 双号' + oe.even_pct + '%';
                        if (oe.recent_streak >= 2) {
                            html += ' | 近期连续' + (oe.recent_type === 'odd' ? '单号' : '双号') + oe.recent_streak + '期';
                            html += '（平均连续' + (oe.recent_type === 'odd' ? oe.avg_odd_streak : oe.avg_even_streak) + '期）';
                        }
                        html += '</div>';
                    }

                    // 大小统计
                    if (analysis.bigsmall_stats) {
                        var bs = analysis.bigsmall_stats;
                        html += '<div style="margin-bottom:5px;"><b>大小规律：</b> 大号' + bs.big_pct + '% / 小号' + bs.small_pct + '%';
                        if (bs.recent_streak >= 2) {
                            html += ' | 近期连续' + (bs.recent_type === 'big' ? '大号' : '小号') + bs.recent_streak + '期';
                            html += '（平均连续' + (bs.recent_type === 'big' ? bs.avg_big_streak : bs.avg_small_streak) + '期）';
                        }
                        html += '</div>';
                    }

                    // 走势方向
                    if (analysis.trend_direction) {
                        var td = analysis.trend_direction;
                        var trendNames = { 'ascending': '上升（号码减小）', 'descending': '下降（号码增大）', 'jump': '跳跃震荡' };
                        html += '<div style="margin-bottom:5px;"><b>走势方向：</b> ' + trendNames[td.trend_type];
                        html += ' | 强度' + (td.trend_strength * 100).toFixed(0) + '% | 平均变化' + td.avg_change + '</div>';
                    }

                    // 上期属性
                    html += '<div><b>上期属性：</b> 区域[' + (analysis.last_zone || '') + '] 尾数[' + (analysis.last_tail || '') + '] 首号[' + (analysis.last_head || '') + ']</div>';
                    html += '</div>';
                }

                // 遗漏统计信息（V2和V3版本）
                if (analysis.omit_stats && (version === 'v2' || version === 'v3')) {
                    html += '<div style="background:#fce4ec;border:1px solid #e91e63;border-radius:6px;padding:10px;margin-bottom:15px;font-size:12px;">';
                    html += '<b>遗漏值统计：</b> 平均遗漏 ' + (analysis.omit_stats.avg || 0).toFixed(1) + ' 期 | 最大遗漏 ' + (analysis.omit_stats.max || 0) + ' 期 | 期望频率 ' + analysis.expected_freq + '';
                    html += '</div>';
                }

                // 命中结果（验证模式下显示）
                if (hitInfo && actualResult) {
                    var hitBgColor = hitInfo.hit ? '#d4edda' : '#f8d7da';
                    var hitBorderColor = hitInfo.hit ? '#28a745' : '#dc3545';
                    var hitTitleColor = hitInfo.hit ? '#155724' : '#721c24';
                    var actualColorHex = Controller.api.getColorByNum(hitInfo.actual_num);

                    html += '<div style="background:' + hitBgColor + ';border:2px solid ' + hitBorderColor + ';border-radius:6px;padding:12px;margin-bottom:15px;">';
                    html += '<div style="font-size:14px;font-weight:bold;color:' + hitTitleColor + ';margin-bottom:8px;">';
                    if (hitInfo.hit) {
                        html += '<i class="fa fa-check-circle"></i> 预测命中！排名第 ' + hitInfo.rank + ' 位';
                    } else {
                        html += '<i class="fa fa-times-circle"></i> 未命中（实际号码不在预测Top' + predictions.length + '中）';
                    }
                    html += '</div>';
                    html += '<div style="display:flex;align-items:center;gap:15px;">';
                    html += '<div style="font-size:12px;">期号 <b>' + hitInfo.actual_expect + '</b> 实际开奖：</div>';
                    html += '<span style="display:inline-block;width:40px;height:40px;line-height:40px;text-align:center;border-radius:50%;color:#fff;background-color:' + actualColorHex + ';font-weight:bold;font-size:18px;">' + hitInfo.actual_num + '</span>';
                    html += '<div style="font-size:12px;">' + hitInfo.actual_animal + ' / ' + hitInfo.actual_color + '</div>';
                    html += '</div></div>';
                }

                // 预测号码列表
                var topCount = predictions.length;
                html += '<div style="background:#e8f5e9;border:1px solid #4caf50;border-radius:6px;padding:12px;margin-bottom:15px;">';
                html += '<div style="font-size:13px;font-weight:bold;color:#2e7d32;margin-bottom:10px;"><i class="fa fa-bullseye"></i> 预测推荐号码（Top ' + topCount + '）</div>';
                html += '<div style="display:flex;flex-wrap:wrap;gap:10px;">';

                for (var i = 0; i < predictions.length; i++) {
                    var p = predictions[i];
                    var colorHex = Controller.api.getColorByNum(p.num);
                    var animal = Controller.api.getAnimalByNum(p.num);
                    // 验证模式下，命中号码高亮
                    var isHit = hitInfo && hitInfo.hit && p.num === hitInfo.actual_num;
                    var cardBg = isHit ? '#fffacd' : '#fff';
                    var cardBorder = isHit ? '2px solid #f39c12' : 'none';
                    var rankBadge = i < 3 ? '<span style="position:absolute;top:-5px;right:-5px;background:#f39c12;color:#fff;font-size:10px;padding:2px 4px;border-radius:50%;">' + (i + 1) + '</span>' : '';

                    // 根据版本显示不同的详情信息
                    var detailInfo = '';
                    if (p.detail) {
                        if (version === 'v3') {
                            // V3版本显示更多维度信息
                            var omitInfo = p.detail.omit || 0;
                            var transScore = p.detail.trans_score || 0;
                            var oddevenScore = p.detail.oddeven_score || 0;
                            var bigsmallScore = p.detail.bigsmall_score || 0;
                            var trendScore = p.detail.trend_score || 0;
                            detailInfo = '<div style="font-size:9px;color:#999;line-height:1.3;">';
                            detailInfo += '遗漏:' + omitInfo + '期 | ';
                            detailInfo += '转移:' + transScore + ' | ';
                            detailInfo += (p.detail.is_odd ? '单' : '双') + ':' + oddevenScore + ' | ';
                            detailInfo += (p.detail.is_big ? '大' : '小') + ':' + bigsmallScore;
                            detailInfo += '</div>';
                        } else if (version === 'v2') {
                            var omitInfo = p.detail.omit || 0;
                            detailInfo = '<div style="font-size:9px;color:#999;">遗漏:' + omitInfo + '期</div>';
                        }
                    }

                    html += '<div style="text-align:center;background:' + cardBg + ';border:' + cardBorder + ';padding:8px;border-radius:8px;min-width:80px;position:relative;box-shadow:0 1px 3px rgba(0,0,0,0.1);">' + rankBadge;
                    html += '<span style="display:inline-block;width:36px;height:36px;line-height:36px;text-align:center;border-radius:50%;color:#fff;background-color:' + colorHex + ';font-weight:bold;font-size:16px;">' + p.num + '</span>';
                    html += '<div style="font-size:10px;color:#666;line-height:1.2;margin-top:2px;">' + animal + '</div>';
                    html += '<div style="font-size:11px;color:#2e7d32;font-weight:bold;">得分:' + p.score + '</div>';

                    // 显示置信度（V3版本）
                    if (version === 'v3' && confidence && confidence.confidence_scores) {
                        var csForNum = confidence.confidence_scores.find(function(c) { return c.num === p.num; });
                        if (csForNum) {
                            // 阈值定义：>=70%高(绿)、50-70%中(橙)、<50%低(红)
                            var confLevel = csForNum.confidence >= 70 ? '高' : (csForNum.confidence >= 50 ? '中' : '低');
                            var confColor = csForNum.confidence >= 70 ? '#4caf50' : (csForNum.confidence >= 50 ? '#ff9800' : '#f44336');
                            html += '<div style="font-size:10px;"><span style="color:' + confColor + ';font-weight:bold;">置信度:' + confLevel + '</span> <span style="color:#666;">(' + csForNum.confidence + '%)</span></div>';
                        }
                    }

                    html += detailInfo;
                    html += '</div>';
                }
                html += '</div></div>';

                html += '</div>';
                $('#predict-result', layero).html(html);
            },

            /**
             * 显示正码关联预测弹窗（修正版）
             * 核心规律：上期正码 → 当期特码
             */
            showNormalRelationDialog: function () {
                var html = '<div style="padding:20px;">' +
                    '<div style="margin-bottom:15px;background:#e8f5e9;border:1px solid #4caf50;padding:10px;border-radius:6px;">' +
                    '  <div style="font-size:13px;font-weight:bold;color:#2e7d32;margin-bottom:8px;"><i class="fa fa-link"></i> 正码关联预测算法（修正版）</div>' +
                    '  <div style="font-size:12px;color:#666;">' +
                    '    <b>核心逻辑</b>：用<b>上期正码(num1-6)</b>预测<b>当期特码(num7)</b><br>' +
                    '    <b>1. 覆盖区间规律（91.44%）</b>：当期特码在上期正码覆盖的细区间内<br>' +
                    '    <b>2. 特码区间转移（77.54%）</b>：基于上期特码区间预测当期特码大区间<br>' +
                    '    <b>3. 双波色预测（69.52%）</b>：当期特码波色在上期正码前2种主导波色内<br>' +
                    '    <b>4. 正码±3距离（59.36%）</b>：当期特码与上期正码某号码距离≤3<br>' +
                    '    <b>5. 尾数±2（50%）</b>：上期正码和值尾数与当期特码尾数差≤2<br>' +
                    '    <b>6. 平均值±10（41.98%）</b>：当期特码在上期正码平均值±10范围' +
                    '  </div>' +
                    '</div>' +
                    '<div class="form-group">' +
                    '  <label>回测期数：</label>' +
                    '  <input type="number" id="nr-periods" class="form-control" value="100" min="30" max="500" style="width:120px;display:inline-block;">' +
                    '  <label style="margin-left:15px;">目标期号（可选）：</label>' +
                    '  <input type="text" id="nr-target" class="form-control" placeholder="如2026120" style="width:150px;display:inline-block;">' +
                    '  <button class="btn btn-primary" id="btn-nr-query" style="margin-left:10px;"><i class="fa fa-search"></i> 查询</button>' +
                    '</div>' +
                    '<div id="nr-result" style="margin-top:15px;"></div>' +
                    '</div>';

                Layer.open({
                    type: 1,
                    title: '正码关联预测（上期正码→当期特码）',
                    area: ['900px', '750px'],
                    content: html,
                    shadeClose: true,
                    success: function (layero, index) {
                        $('#btn-nr-query', layero).on('click', function () {
                            Controller.api.queryNormalRelation(layero);
                        });
                        // 自动执行一次查询
                        Controller.api.queryNormalRelation(layero);
                    }
                });
            },

            /**
             * 查询正码关联预测
             */
            queryNormalRelation: function (layero) {
                var $btn = $('#btn-nr-query', layero);
                $btn.prop('disabled', true);
                $('#nr-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> 正在分析...</div>');

                var periods = parseInt($('#nr-periods', layero).val()) || 100;
                var targetExpect = $('#nr-target', layero).val().trim();

                $.ajax({
                    url: 'history/predictByNormalRelation',
                    type: 'GET',
                    data: { periods: periods, target_expect: targetExpect },
                    dataType: 'json',
                    success: function (ret) {
                        if (ret.code == 1) {
                            Controller.api.renderNormalRelation(ret.data, layero);
                        } else {
                            $('#nr-result', layero).html('<div class="alert alert-danger">' + (ret.msg || '查询失败') + '</div>');
                        }
                    },
                    error: function () {
                        $('#nr-result', layero).html('<div class="alert alert-danger">查询失败</div>');
                    },
                    complete: function () {
                        $btn.prop('disabled', false);
                    }
                });
            },

            /**
             * 渲染正码关联预测结果
             */
            renderNormalRelation: function (data, layero) {
                if (!data || !data.predictions || data.predictions.length === 0) {
                    $('#nr-result', layero).html('<div class="alert alert-info">无预测结果</div>');
                    return;
                }

                var analysis = data.analysis || {};
                var predictions = data.predictions;
                var hitInfo = data.hit_info;
                var backtest = data.backtest;

                var html = '';

                // 分析信息
                html += '<div style="background:#f5f5f5;padding:10px;border-radius:6px;margin-bottom:15px;">';
                html += '<div style="font-size:13px;font-weight:bold;margin-bottom:8px;"><i class="fa fa-info-circle"></i> 上期开奖信息（预测基准）</div>';
                html += '<div style="font-size:12px;">';
                html += '期号：<b>' + analysis.last_expect + '</b> | ';
                html += '正码：<b>' + analysis.last_normals.join(', ') + '</b> | ';
                html += '特码：<b>' + analysis.last_special + '</b><br>';
                html += '正码范围：' + analysis.normal_min + ' ~ ' + analysis.normal_max + ' | ';
                html += '平均值：' + analysis.normal_avg + ' | ';
                html += '和值：' + analysis.normal_sum + '<br>';
                html += '正码波色：<b>' + (analysis.top2_colors || analysis.normal_colors || []).join('/') + '</b> | ';
                html += '覆盖区间：' + (analysis.normal_fine_zones || analysis.normal_zones || []).map(function(z) {
                    return ['1-10','11-20','21-30','31-40','41-49'][z];
                }).join(',');
                html += '</div></div>';

                // 预测号码展示
                html += '<div style="margin-bottom:15px;">';
                html += '<div style="font-size:13px;font-weight:bold;margin-bottom:8px;"><i class="fa fa-star"></i> 推荐号码（Top 15）</div>';
                html += '<div style="display:flex;flex-wrap:wrap;gap:8px;">';

                for (var i = 0; i < predictions.length; i++) {
                    var p = predictions[i];
                    var colorHex = Controller.api.getColorByNum(p.num);
                    var animal = Controller.api.animalMap[p.num] || '';

                    var bgColor = '#fff';
                    var borderColor = '1px solid #ddd';
                    if (i < 5) {
                        bgColor = '#fff8e1';
                        borderColor = '2px solid #ffc107';
                    }

                    html += '<div style="background:' + bgColor + ';border:' + borderColor + ';padding:8px;border-radius:8px;min-width:90px;text-align:center;">';
                    html += '<span style="display:inline-block;width:36px;height:36px;line-height:36px;border-radius:50%;color:#fff;background:' + colorHex + ';font-weight:bold;font-size:16px;">' + p.num + '</span>';
                    html += '<div style="font-size:10px;color:#666;">' + animal + '</div>';
                    html += '<div style="font-size:11px;color:#2e7d32;font-weight:bold;">得分:' + p.score + '</div>';
                    html += '<div style="font-size:9px;color:#999;">';
                    html += (p.color_in_top2 || p.color_match) ? '✓波色 ' : '';
                    html += '距离' + (p.min_distance || 0);
                    html += '</div>';
                    html += '</div>';
                }
                html += '</div></div>';

                // 规律命中情况（如果有回测）
                if (backtest) {
                    html += '<div style="background:#e3f2fd;padding:10px;border-radius:6px;margin-bottom:15px;">';
                    html += '<div style="font-size:13px;font-weight:bold;margin-bottom:8px;"><i class="fa fa-chart-line"></i> 回测验证（最近' + backtest.periods + '期）</div>';
                    html += '<div style="font-size:12px;">';
                    html += '命中率（Top15内）：<b style="color:#2e7d32;">' + backtest.hit_rate + '%</b> (' + backtest.hits + '/' + backtest.periods + ')<br>';
                    html += '平均排名：<b>' + backtest.avg_rank + '</b> / 49<br>';
                    html += '<span style="color:#666;font-size:11px;">注：命中率越高越好，平均排名越低越好</span>';
                    html += '</div>';

                    // 显示前50期命中详情表格
                    if (backtest.details && backtest.details.length > 0) {
                        html += '<div style="margin-top:10px;max-height:300px;overflow-y:auto;">';
                        html += '<table class="table table-striped table-bordered" style="font-size:11px;">';
                        html += '<thead><tr><th>期号</th><th>特码</th><th>排名</th><th>命中</th></tr></thead>';
                        html += '<tbody>';
                        for (var d = 0; d < backtest.details.length; d++) {
                            var det = backtest.details[d];
                            var hitBadge = det.hit ? '<span style="color:#2e7d32;font-weight:bold;">✓</span>' : '<span style="color:#c62828;">✗</span>';
                            var rankColor = det.rank <= 5 ? '#2e7d32' : (det.rank <= 15 ? '#ff9800' : '#c62828');
                            html += '<tr>';
                            html += '<td>' + det.expect + '</td>';
                            html += '<td>' + det.actual + '</td>';
                            html += '<td><b style="color:' + rankColor + ';">' + det.rank + '</b></td>';
                            html += '<td>' + hitBadge + '</td>';
                            html += '</tr>';
                        }
                        html += '</tbody></table>';
                        html += '</div>';
                    }
                    html += '</div>';
                }

                // 实际命中情况（如果有目标期号）
                if (hitInfo) {
                    var hitBg = hitInfo.hit ? '#e8f5e9' : '#ffebee';
                    var hitColor = hitInfo.hit ? '#2e7d32' : '#c62828';
                    html += '<div style="background:' + hitBg + ';padding:10px;border-radius:6px;margin-bottom:15px;">';
                    html += '<div style="font-size:13px;font-weight:bold;color:' + hitColor + ';">';
                    html += hitInfo.hit ? '✓ 命中！排名：' + hitInfo.rank_in_top + ' / 15' : '✗ 未命中，排名：' + hitInfo.rank_in_all + ' / 49';
                    html += '</div>';
                    html += '<div style="font-size:12px;">实际特码：<b>' + hitInfo.actual_num + '</b> (' + hitInfo.actual_color + '/' + hitInfo.actual_animal + ') 期号：' + hitInfo.actual_expect + '</div>';
                    html += '</div>';
                }

                // 规律说明表
                if (analysis.rules) {
                    html += '<div style="margin-top:15px;">';
                    html += '<div style="font-size:13px;font-weight:bold;margin-bottom:8px;"><i class="fa fa-table"></i> 规律命中率表</div>';
                    html += '<table class="table table-bordered" style="font-size:12px;">';
                    html += '<tr><th>规律名称</th><th>命中率</th><th>说明</th></tr>';
                    for (var r = 0; r < analysis.rules.length; r++) {
                        var rule = analysis.rules[r];
                        html += '<tr><td>' + rule.name + '</td><td><b style="color:#2e7d32;">' + rule.rate + '</b></td><td>' + rule.desc + '</td></tr>';
                    }
                    html += '</table></div>';
                }

                $('#nr-result', layero).html(html);
            }
        }
    };
    return Controller;
});
