<?php
/**
 * WinRacing V45.0 — Chairman Ensemble AI FIXED
 * PHP 7.4 / MySQLi / Standalone
 * Reads V44 production table and writes V45 ensemble tables.
 */
@set_time_limit(1200);
@ini_set('memory_limit','1536M');

require_once __DIR__ . '/config/config.php';
if (!isset($conn) && isset($db)) { $conn = $db; }
if (!isset($conn) || !($conn instanceof mysqli)) { die('DB connection not found.'); }
$conn->set_charset('utf8mb4');

function v45_h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
function v45_q($db,$s){ return "'" . $db->real_escape_string((string)$s) . "'"; }
function v45_n($v,$d=0){ return is_numeric($v) ? (float)$v : $d; }
function v45_i($v,$d=0){ return is_numeric($v) ? (int)$v : $d; }
function v45_rows($db,$sql){ $a=array(); $r=@$db->query($sql); if($r){ while($x=$r->fetch_assoc()) $a[]=$x; } return $a; }
function v45_table_exists($db,$t){ $r=@$db->query("SHOW TABLES LIKE ".v45_q($db,$t)); return $r && $r->num_rows>0; }
function v45_run_key(){ return date('YmdHis').'_'.mt_rand(1000,9999); }
function v45_clamp($v,$a,$b){ return max($a,min($b,$v)); }

function v45_ensure($db){
    $db->query("CREATE TABLE IF NOT EXISTS wr_chairman_ensemble_ai_v45 (
        id INT AUTO_INCREMENT PRIMARY KEY,
        ensemble_key VARCHAR(700) NOT NULL,
        run_key VARCHAR(80) NOT NULL,
        race_date DATE NULL,
        venue VARCHAR(80) NOT NULL DEFAULT '',
        race_num INT NOT NULL DEFAULT 0,
        horse_num VARCHAR(20) NOT NULL DEFAULT '',
        horse_name VARCHAR(180) NOT NULL DEFAULT '',
        v44_rank INT NOT NULL DEFAULT 0,
        v44_score DECIMAL(10,4) NOT NULL DEFAULT 0,
        ensemble_score DECIMAL(10,4) NOT NULL DEFAULT 0,
        confidence_score DECIMAL(10,4) NOT NULL DEFAULT 0,
        rci_score DECIMAL(10,4) NOT NULL DEFAULT 0,
        model_votes INT NOT NULL DEFAULT 0,
        yes_votes INT NOT NULL DEFAULT 0,
        no_votes INT NOT NULL DEFAULT 0,
        role_tag VARCHAR(80) NOT NULL DEFAULT '',
        final_pick_type VARCHAR(80) NOT NULL DEFAULT '',
        final_decision VARCHAR(30) NOT NULL DEFAULT 'WATCH',
        cold_flag TINYINT NOT NULL DEFAULT 0,
        sixup_flag TINYINT NOT NULL DEFAULT 0,
        trio_flag TINYINT NOT NULL DEFAULT 0,
        reason_zh TEXT NULL,
        json_payload LONGTEXT NULL,
        created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        UNIQUE KEY uk_v45_ensemble(ensemble_key),
        KEY idx_v45_race(race_date,venue,race_num),
        KEY idx_v45_run(run_key),
        KEY idx_v45_score(ensemble_score)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");

    $db->query("CREATE TABLE IF NOT EXISTS wr_chairman_race_decision_v45 (
        id INT AUTO_INCREMENT PRIMARY KEY,
        decision_key VARCHAR(700) NOT NULL,
        run_key VARCHAR(80) NOT NULL,
        race_date DATE NULL,
        venue VARCHAR(80) NOT NULL DEFAULT '',
        race_num INT NOT NULL DEFAULT 0,
        race_confidence_index DECIMAL(10,4) NOT NULL DEFAULT 0,
        banker_horse VARCHAR(20) NOT NULL DEFAULT '',
        three_legs VARCHAR(120) NOT NULL DEFAULT '',
        sixup_top2 VARCHAR(120) NOT NULL DEFAULT '',
        trio_suggestion VARCHAR(160) NOT NULL DEFAULT '',
        cold_legs VARCHAR(160) NOT NULL DEFAULT '',
        race_mode VARCHAR(80) NOT NULL DEFAULT '',
        decision_summary TEXT NULL,
        json_payload LONGTEXT NULL,
        created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        UNIQUE KEY uk_v45_decision(decision_key),
        KEY idx_v45_decision_race(race_date,venue,race_num),
        KEY idx_v45_decision_run(run_key)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");

    $db->query("CREATE TABLE IF NOT EXISTS wr_chairman_ensemble_run_log_v45 (
        id INT AUTO_INCREMENT PRIMARY KEY,
        run_key VARCHAR(80) NOT NULL,
        race_date DATE NULL,
        venue VARCHAR(80) NOT NULL DEFAULT '',
        status VARCHAR(30) NOT NULL DEFAULT 'DONE',
        source_rows INT NOT NULL DEFAULT 0,
        ensemble_rows INT NOT NULL DEFAULT 0,
        decision_rows INT NOT NULL DEFAULT 0,
        message TEXT NULL,
        created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        KEY idx_v45_log_run(run_key),
        KEY idx_v45_log_date(race_date,venue)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
}

function v45_source($db,$raceDate,$venue='',$raceNum=0){
    if(!v45_table_exists($db,'wr_chairman_production_score_v44')) return array();
    $w=array("race_date=".v45_q($db,$raceDate));
    if($venue!=='') $w[]="venue=".v45_q($db,$venue);
    if(v45_i($raceNum)>0) $w[]="race_num=".v45_i($raceNum);
    return v45_rows($db,"SELECT * FROM wr_chairman_production_score_v44 WHERE ".implode(' AND ',$w)." ORDER BY race_num ASC, race_rank ASC, calibrated_score DESC");
}

function v45_vote_models($r){
    $score=v45_n($r['calibrated_score']);
    $rank=v45_i($r['race_rank']);
    $risk=v45_n($r['risk_score']);
    $conf=v45_n($r['confidence_score']);
    $place=v45_n($r['place_score']);
    $q=v45_n($r['q_score']);
    $trio=v45_n($r['trio_score']);
    $role=(string)$r['role_tag'];

    $votes=array();
    $votes['V44Score']=($score>=70)?'YES':(($score<50)?'NO':'ABSTAIN');
    $votes['Rank']=($rank<=2)?'YES':(($rank>=7)?'NO':'ABSTAIN');
    $votes['RiskControl']=($risk<55)?'YES':(($risk>=70)?'NO':'ABSTAIN');
    $votes['Confidence']=($conf>=60)?'YES':(($conf<40)?'NO':'ABSTAIN');
    $votes['QPower']=($q>=60 || $place>=60)?'YES':(($q<35 && $place<35)?'NO':'ABSTAIN');
    $votes['TrioPower']=($trio>=55)?'YES':(($trio<32)?'NO':'ABSTAIN');
    $votes['Role']=in_array($role,array('主席膽','Q膽','主力腳'),true)?'YES':($role==='觀察'?'NO':'ABSTAIN');

    $yes=0;$no=0;$abs=0;
    foreach($votes as $v){ if($v==='YES')$yes++; elseif($v==='NO')$no++; else $abs++; }

    $voteScore=($yes*4.5)-($no*4.0)+($abs*0.8);
    $ensemble=v45_clamp($score*0.50+$conf*0.18+(100-$risk)*0.14+$q*0.09+$trio*0.09+$voteScore,0,100);
    $rci=v45_clamp($ensemble*0.55+$conf*0.25+(100-$risk)*0.20,0,100);

    return array($votes,$yes,$no,$abs,$ensemble,$rci);
}

function v45_build($db,$raceDate,$venue='',$raceNum=0){
    v45_ensure($db);
    $src=v45_source($db,$raceDate,$venue,$raceNum);
    $run=v45_run_key();

    $by=array();
    foreach($src as $r){
        $g=$r['race_date'].'|'.$r['venue'].'|'.$r['race_num'];
        if(!isset($by[$g])) $by[$g]=array();
        $by[$g][]=$r;
    }

    $ensembleRows=array();
    $decisionRows=array();

    foreach($by as $g=>$rows){
        $tmp=array();
        foreach($rows as $r){
            list($votes,$yes,$no,$abs,$ensemble,$rci)=v45_vote_models($r);
            $rank=v45_i($r['race_rank']);
            $risk=v45_n($r['risk_score']);
            $cold=($rank>=4 && $ensemble>=55 && $risk<68)?1:0;
            $six=($rank<=2 && $ensemble>=58)?1:0;
            $trio=($rank<=4 && $ensemble>=48)?1:0;

            $pick='觀察';
            if($rank==1 && $ensemble>=72 && $risk<60) $pick='一膽';
            elseif($rank<=2 && $ensemble>=62) $pick='六環彩Top2/Q膽';
            elseif($rank<=4 && $ensemble>=52) $pick='三腳/單T腳';
            elseif($cold) $pick='爆冷腳';

            $decision=($ensemble>=74 && $risk<58)?'BUY':(($ensemble>=60)?'COVER':(($ensemble<45||$risk>=75)?'SKIP':'WATCH'));
            $payload=array('votes'=>$votes,'ensemble_score'=>round($ensemble,2),'rci'=>round($rci,2),'pick_type'=>$pick);

            $tmp[]=array('src'=>$r,'votes'=>$votes,'yes'=>$yes,'no'=>$no,'abs'=>$abs,'ensemble'=>$ensemble,'rci'=>$rci,'cold'=>$cold,'six'=>$six,'trio'=>$trio,'pick'=>$pick,'decision'=>$decision,'payload'=>$payload);
        }

        usort($tmp,function($a,$b){ return $a['ensemble']<$b['ensemble']?1:-1; });

        foreach($tmp as $x){
            $r=$x['src'];
            $ensembleRows[]=array(
                'ensemble_key'=>sha1($run.'|V45|'.$r['race_date'].'|'.$r['venue'].'|'.$r['race_num'].'|'.$r['horse_num']),
                'run_key'=>$run,'race_date'=>$r['race_date'],'venue'=>$r['venue'],'race_num'=>v45_i($r['race_num']),
                'horse_num'=>(string)$r['horse_num'],'horse_name'=>(string)$r['horse_name'],
                'v44_rank'=>v45_i($r['race_rank']),'v44_score'=>v45_n($r['calibrated_score']),
                'ensemble_score'=>$x['ensemble'],'confidence_score'=>v45_n($r['confidence_score']),'rci_score'=>$x['rci'],
                'model_votes'=>count($x['votes']),'yes_votes'=>$x['yes'],'no_votes'=>$x['no'],
                'role_tag'=>(string)$r['role_tag'],'final_pick_type'=>$x['pick'],'final_decision'=>$x['decision'],
                'cold_flag'=>$x['cold'],'sixup_flag'=>$x['six'],'trio_flag'=>$x['trio'],
                'reason_zh'=>'V45 Ensemble：#'.$r['horse_num'].' '.$r['horse_name'].'｜'.$x['pick'].'｜Ensemble='.number_format($x['ensemble'],1).'｜RCI='.number_format($x['rci'],1).'｜YES='.$x['yes'].' NO='.$x['no'].'。',
                'json_payload'=>json_encode($x['payload'],JSON_UNESCAPED_UNICODE)
            );
        }

        $banker=''; $legs=array(); $six=array(); $trio=array(); $cold=array(); $rciAvg=0;
        foreach($tmp as $idx=>$x){
            $r=$x['src'];
            $rciAvg+=$x['rci'];
            if($banker==='' && $x['pick']==='一膽') $banker=$r['horse_num'];
            if(count($legs)<3 && $x['trio']) $legs[]=$r['horse_num'];
            if(count($six)<2 && $x['six']) $six[]=$r['horse_num'];
            if(count($trio)<4 && $x['trio']) $trio[]=$r['horse_num'];
            if(count($cold)<2 && $x['cold']) $cold[]=$r['horse_num'];
        }
        if($banker==='' && count($tmp)) $banker=$tmp[0]['src']['horse_num'];
        $rciAvg=count($tmp)?$rciAvg/count($tmp):0;
        list($rd,$ve,$rn)=explode('|',$g);
        $mode=$rciAvg>=70?'主攻場':($rciAvg>=58?'穩健場':($rciAvg>=45?'混戰場':'低信心場'));
        $summary='V45：R'.$rn.' '.$mode.'｜一膽 '.$banker.'｜三腳 '.implode(',',$legs).'｜六環Top2 '.implode(',',$six).'｜冷腳 '.implode(',',$cold);

        $decisionRows[]=array(
            'decision_key'=>sha1($run.'|DECISION|'.$g),
            'run_key'=>$run,'race_date'=>$rd,'venue'=>$ve,'race_num'=>v45_i($rn),
            'race_confidence_index'=>$rciAvg,'banker_horse'=>(string)$banker,
            'three_legs'=>implode(',',$legs),'sixup_top2'=>implode(',',$six),'trio_suggestion'=>implode(',',$trio),
            'cold_legs'=>implode(',',$cold),'race_mode'=>$mode,'decision_summary'=>$summary,
            'json_payload'=>json_encode(array('banker'=>$banker,'legs'=>$legs,'sixup'=>$six,'trio'=>$trio,'cold'=>$cold,'mode'=>$mode),JSON_UNESCAPED_UNICODE)
        );
    }

    $db->begin_transaction();
    foreach($ensembleRows as $r){
        $cols=array_keys($r); $vals=array();
        foreach($cols as $c){ $vals[]=is_numeric($r[$c])?$r[$c]:v45_q($db,$r[$c]); }
        $upd=array('v44_score','ensemble_score','confidence_score','rci_score','model_votes','yes_votes','no_votes','role_tag','final_pick_type','final_decision','cold_flag','sixup_flag','trio_flag','reason_zh','json_payload');
        $u=array(); foreach($upd as $c){$u[]=$c.'=VALUES('.$c.')';}
        $db->query("INSERT INTO wr_chairman_ensemble_ai_v45 (".implode(',',$cols).") VALUES (".implode(',',$vals).") ON DUPLICATE KEY UPDATE ".implode(',',$u));
    }
    foreach($decisionRows as $r){
        $cols=array_keys($r); $vals=array();
        foreach($cols as $c){ $vals[]=is_numeric($r[$c])?$r[$c]:v45_q($db,$r[$c]); }
        $upd=array('race_confidence_index','banker_horse','three_legs','sixup_top2','trio_suggestion','cold_legs','race_mode','decision_summary','json_payload');
        $u=array(); foreach($upd as $c){$u[]=$c.'=VALUES('.$c.')';}
        $db->query("INSERT INTO wr_chairman_race_decision_v45 (".implode(',',$cols).") VALUES (".implode(',',$vals).") ON DUPLICATE KEY UPDATE ".implode(',',$u));
    }
    $msg='Built V45 Ensemble AI: source '.count($src).' | ensemble '.count($ensembleRows).' | races '.count($decisionRows);
    $db->query("INSERT INTO wr_chairman_ensemble_run_log_v45 (run_key,race_date,venue,status,source_rows,ensemble_rows,decision_rows,message) VALUES (".v45_q($db,$run).",".v45_q($db,$raceDate).",".v45_q($db,$venue).",'DONE',".count($src).",".count($ensembleRows).",".count($decisionRows).",".v45_q($db,$msg).")");
    $db->commit();

    return array('run_key'=>$run,'source_rows'=>count($src),'ensemble_rows'=>count($ensembleRows),'decision_rows'=>count($decisionRows),'message'=>$msg);
}

$raceDate=isset($_GET['race_date'])?$_GET['race_date']:(isset($_GET['date'])?$_GET['date']:date('Y-m-d'));
$venue=isset($_GET['venue'])?$_GET['venue']:'';
$raceNum=isset($_GET['race_num'])?v45_i($_GET['race_num']):0;
$action=isset($_GET['action'])?$_GET['action']:'view';

$msg='';
if($action==='build'){
    $res=v45_build($conn,$raceDate,$venue,$raceNum);
    $msg=$res['message'];
}

v45_ensure($conn);
$decisions=v45_rows($conn,"SELECT * FROM wr_chairman_race_decision_v45 WHERE race_date=".v45_q($conn,$raceDate).($venue!==''?" AND venue=".v45_q($conn,$venue):"")." ORDER BY race_num ASC");
$rankings=v45_rows($conn,"SELECT * FROM wr_chairman_ensemble_ai_v45 WHERE race_date=".v45_q($conn,$raceDate).($venue!==''?" AND venue=".v45_q($conn,$venue):"")." ORDER BY race_num ASC, ensemble_score DESC LIMIT 300");
$sourceCount=count(v45_source($conn,$raceDate,$venue,$raceNum));
?>
<!doctype html>
<html lang="zh-Hant">
<head>
<meta charset="utf-8">
<title>WinRacing V45 Chairman Ensemble AI</title>
<style>
body{margin:0;background:#050914;color:#e7fbff;font-family:Arial,'Microsoft JhengHei';font-size:13px}.wrap{padding:22px;max-width:1760px;margin:auto}
.hero{border:1px solid #31486b;border-radius:18px;background:radial-gradient(circle at top left,#3b2807,#0b1325 45%,#050914);padding:22px 28px;margin-bottom:16px}
.title{font-size:31px;font-weight:900}.gold{color:#ffc107}.ok{color:#32ffa7}.bad{color:#ff5a86}.panel{border:1px solid #283c59;border-radius:14px;background:#0c1423;margin-bottom:18px;overflow:hidden}.panel h3{margin:0;padding:14px 16px;border-bottom:1px solid #283c59}.body{padding:14px 16px}
.inp{background:#091222;color:#fff;border:1px solid #314966;border-radius:5px;padding:9px}.btn{background:#ffc107;color:#111;border:0;border-radius:6px;padding:10px 14px;font-weight:900;text-decoration:none}
table{width:100%;border-collapse:collapse}th,td{border-bottom:1px solid #243650;padding:8px;vertical-align:top;font-size:12px}th{background:#101a2c;color:#d7ecff;position:sticky;top:0}.scroll{overflow:auto;max-height:650px}
.badge{display:inline-block;border-radius:999px;padding:4px 9px;font-weight:900;font-size:11px}.buy{background:#18b26b;color:#001b12}.cover{background:#2dd4bf;color:#001b12}.watch{background:#ffc107;color:#111}.skip{background:#ff3860;color:#fff}
</style>
</head>
<body>
<div class="wrap">
<div class="hero"><div class="title">🏆 V45.0 — Chairman <span class="gold">Ensemble AI</span></div><div>V44 Production Ranking → Multi Model Voting → RCI → 一膽 / 三腳 / 六環 Top2 / 單T / 爆冷腳</div></div>
<?php if($msg): ?><div class="panel"><div class="body ok">✅ <?=v45_h($msg)?></div></div><?php endif; ?>
<div class="panel"><h3>Build V45 Ensemble</h3><div class="body">
<form method="get">
Race Date <input class="inp" type="date" name="race_date" value="<?=v45_h($raceDate)?>">
Venue <input class="inp" name="venue" value="<?=v45_h($venue)?>">
Race # <input class="inp" type="number" name="race_num" value="<?=v45_h($raceNum)?>">
<button class="btn" name="action" value="build">Build V45</button>
</form>
<p>V44 Source Rows: <span class="gold"><?=v45_h($sourceCount)?></span>｜V45 Ranking Rows: <span class="gold"><?=count($rankings)?></span>｜Decision Rows: <span class="gold"><?=count($decisions)?></span></p>
</div></div>

<div class="panel"><h3>Race Decision</h3><div class="scroll"><table>
<tr><th>Race</th><th>RCI</th><th>Mode</th><th>一膽</th><th>三腳</th><th>六環Top2</th><th>單T</th><th>爆冷腳</th><th>Summary</th></tr>
<?php foreach($decisions as $r): ?>
<tr><td>R<?=v45_h($r['race_num'])?></td><td class="gold"><?=number_format(v45_n($r['race_confidence_index']),1)?></td><td><?=v45_h($r['race_mode'])?></td><td class="gold"><?=v45_h($r['banker_horse'])?></td><td><?=v45_h($r['three_legs'])?></td><td><?=v45_h($r['sixup_top2'])?></td><td><?=v45_h($r['trio_suggestion'])?></td><td><?=v45_h($r['cold_legs'])?></td><td><?=v45_h($r['decision_summary'])?></td></tr>
<?php endforeach; ?>
</table></div></div>

<div class="panel"><h3>Horse Ensemble Ranking</h3><div class="scroll"><table>
<tr><th>Race</th><th>Horse</th><th>Name</th><th>V44 Rank</th><th>V44</th><th>Ensemble</th><th>RCI</th><th>YES/NO</th><th>Pick Type</th><th>Decision</th><th>Reason</th></tr>
<?php foreach($rankings as $r): $cls=strtolower($r['final_decision']); ?>
<tr><td>R<?=v45_h($r['race_num'])?></td><td class="gold"><?=v45_h($r['horse_num'])?></td><td><?=v45_h($r['horse_name'])?></td><td>#<?=v45_h($r['v44_rank'])?></td><td><?=number_format(v45_n($r['v44_score']),1)?></td><td class="gold"><?=number_format(v45_n($r['ensemble_score']),1)?></td><td><?=number_format(v45_n($r['rci_score']),1)?></td><td><?=v45_h($r['yes_votes'])?> / <?=v45_h($r['no_votes'])?></td><td><?=v45_h($r['final_pick_type'])?></td><td><span class="badge <?=$cls?>"><?=v45_h($r['final_decision'])?></span></td><td><?=v45_h($r['reason_zh'])?></td></tr>
<?php endforeach; ?>
</table></div></div>
</div>
</body>
</html>
