<?php
/**
 * WinRacing V51 — Supreme Decision Engine
 * PHP 7.4 compatible. Read-only DB integration: no CREATE/ALTER/DROP.
 *
 * Source: V50 Ultimate Decision tables only.
 * Purpose: add a governance/calibration layer without changing any V44-V50 score.
 */
class ChairmanSupremeDecisionEngineV51
{
    private $db;
    private $snapshotDir;

    public function __construct(mysqli $db, $snapshotDir = '')
    {
        $this->db = $db;
        $this->db->set_charset('utf8mb4');
        $this->snapshotDir = $snapshotDir !== ''
            ? rtrim($snapshotDir, '/\\')
            : dirname(__DIR__, 2) . '/runtime/cache/v51';
    }

    private function q($value)
    {
        return "'" . $this->db->real_escape_string((string)$value) . "'";
    }

    private function num($value, $default = 0.0)
    {
        return is_numeric($value) ? (float)$value : (float)$default;
    }

    private function clamp($value, $min = 0.0, $max = 100.0)
    {
        return max($min, min($max, $value));
    }

    private function rows($sql)
    {
        $rows = array();
        $result = $this->db->query($sql);
        if ($result instanceof mysqli_result) {
            while ($row = $result->fetch_assoc()) {
                $rows[] = $row;
            }
            $result->free();
        }
        return $rows;
    }

    private function tableExists($table)
    {
        $safe = $this->db->real_escape_string((string)$table);
        $result = $this->db->query("SHOW TABLES LIKE '" . $safe . "'");
        if (!($result instanceof mysqli_result)) {
            return false;
        }
        $exists = $result->num_rows > 0;
        $result->free();
        return $exists;
    }

    private function latestV50Run($date, $venue)
    {
        if (!$this->tableExists('wr_chairman_ultimate_run_log_v50')) {
            return '';
        }
        $where = array('race_date=' . $this->q($date));
        if ($venue !== '') {
            $where[] = 'venue=' . $this->q($venue);
        }
        $rows = $this->rows(
            'SELECT run_key FROM wr_chairman_ultimate_run_log_v50 WHERE ' .
            implode(' AND ', $where) .
            ' ORDER BY created_at DESC,id DESC LIMIT 1'
        );
        return isset($rows[0]['run_key']) ? (string)$rows[0]['run_key'] : '';
    }

    private function countCsv($value)
    {
        $value = trim((string)$value);
        if ($value === '') {
            return 0;
        }
        $parts = preg_split('/\s*[,，|]\s*/u', $value, -1, PREG_SPLIT_NO_EMPTY);
        return is_array($parts) ? count($parts) : 0;
    }

    private function tagBonus($tag)
    {
        if ($tag === 'KING') {
            return 4.0;
        }
        if ($tag === 'MAIN_BANKER') {
            return 2.0;
        }
        return 0.0;
    }

    private function stateAdjustment($state)
    {
        if ($state === 'PRIMARY') {
            return 4.0;
        }
        if ($state === 'STRONG') {
            return 2.0;
        }
        if ($state === 'COVER') {
            return 0.5;
        }
        if ($state === 'MIXED') {
            return -5.0;
        }
        return -3.0;
    }

    private function decisionTier($score, $stars, $state)
    {
        if ($score >= 78.0 && $stars >= 5 && $state === 'PRIMARY') {
            return 'SUPREME_BANKER';
        }
        if ($score >= 68.0 && $stars >= 4 && in_array($state, array('PRIMARY', 'STRONG'), true)) {
            return 'PRIME_BANKER';
        }
        if ($score >= 58.0 && $stars >= 3) {
            return 'COVER';
        }
        if ($score >= 48.0) {
            return 'WATCH';
        }
        return 'SKIP';
    }

    private function actionForTier($tier)
    {
        $map = array(
            'SUPREME_BANKER' => 'PRIMARY',
            'PRIME_BANKER' => 'STRONG',
            'COVER' => 'COVER',
            'WATCH' => 'WATCH',
            'SKIP' => 'SKIP'
        );
        return isset($map[$tier]) ? $map[$tier] : 'WATCH';
    }

    private function calibrateRace(array $race)
    {
        $ultimate = $this->num(isset($race['ultimate_confidence']) ? $race['ultimate_confidence'] : 0);
        $consensus = $this->num(isset($race['consensus_pct']) ? $race['consensus_pct'] : 0);
        $stability = $this->num(isset($race['stability_index']) ? $race['stability_index'] : 0);
        $stars = (int)(isset($race['star_rating']) ? $race['star_rating'] : 1);
        $state = (string)(isset($race['race_state']) ? $race['race_state'] : 'WATCH');
        $tag = (string)(isset($race['final_tag']) ? $race['final_tag'] : 'WATCH');
        $avoidCount = $this->countCsv(isset($race['avoid_horses']) ? $race['avoid_horses'] : '');

        $riskPenalty = min(8.0, $avoidCount * 1.25);
        if ($stars <= 2) {
            $riskPenalty += 3.0;
        }
        if ($state === 'MIXED') {
            $riskPenalty += 2.0;
        }

        $score = $this->clamp(
            ($ultimate * 0.72) +
            ($consensus * 0.14) +
            ($stability * 0.14) +
            $this->tagBonus($tag) +
            $this->stateAdjustment($state) -
            $riskPenalty
        );

        $tier = $this->decisionTier($score, $stars, $state);
        $action = $this->actionForTier($tier);
        $confidenceGap = $score - $ultimate;

        return array(
            'race_num' => (int)(isset($race['race_num']) ? $race['race_num'] : 0),
            'banker_horse' => (string)(isset($race['banker_horse']) ? $race['banker_horse'] : ''),
            'banker_name' => (string)(isset($race['banker_name']) ? $race['banker_name'] : ''),
            'v50_ultimate_confidence' => round($ultimate, 4),
            'consensus_pct' => round($consensus, 4),
            'stability_index' => round($stability, 4),
            'star_rating' => $stars,
            'race_state' => $state,
            'final_tag' => $tag,
            'q_top2' => (string)(isset($race['q_top2']) ? $race['q_top2'] : ''),
            'trio_top4' => (string)(isset($race['trio_top4']) ? $race['trio_top4'] : ''),
            'value_horses' => (string)(isset($race['value_horses']) ? $race['value_horses'] : ''),
            'avoid_horses' => (string)(isset($race['avoid_horses']) ? $race['avoid_horses'] : ''),
            'risk_penalty' => round($riskPenalty, 4),
            'supreme_confidence' => round($score, 4),
            'confidence_gap' => round($confidenceGap, 4),
            'supreme_tier' => $tier,
            'supreme_action' => $action,
            'reason' => 'V51 calibration over V50 output only; V44-V50 score logic unchanged.'
        );
    }

    private function buildSummary(array $races)
    {
        if (!$races) {
            return array();
        }
        usort($races, function ($a, $b) {
            if ($a['supreme_confidence'] == $b['supreme_confidence']) {
                return $a['race_num'] <=> $b['race_num'];
            }
            return ($a['supreme_confidence'] < $b['supreme_confidence']) ? 1 : -1;
        });

        $top = $races[0];
        $top3 = array();
        $strong = array();
        $skip = array();
        $sum = 0.0;
        $sample = array_slice($races, 0, 5);

        foreach ($races as $race) {
            if (count($top3) < 3) {
                $top3[] = 'R' . $race['race_num'] . '#' . $race['banker_horse'];
            }
            if (in_array($race['supreme_tier'], array('SUPREME_BANKER', 'PRIME_BANKER'), true)) {
                $strong[] = 'R' . $race['race_num'] . '#' . $race['banker_horse'];
            }
            if ($race['supreme_tier'] === 'SKIP') {
                $skip[] = 'R' . $race['race_num'];
            }
        }
        foreach ($sample as $race) {
            $sum += (float)$race['supreme_confidence'];
        }
        $portfolio = count($sample) ? $sum / count($sample) : 0.0;
        $state = 'WATCH_DAY';
        if ($portfolio >= 74.0) {
            $state = 'SUPREME_DAY';
        } elseif ($portfolio >= 64.0) {
            $state = 'STRONG_DAY';
        } elseif ($portfolio >= 54.0) {
            $state = 'SELECTIVE_DAY';
        }

        return array(
            'best_banker_race' => $top['race_num'],
            'best_banker_horse' => $top['banker_horse'],
            'best_banker_name' => $top['banker_name'],
            'best_banker_score' => $top['supreme_confidence'],
            'best_banker_tier' => $top['supreme_tier'],
            'top3_bankers' => implode(',', $top3),
            'strong_bankers' => implode(',', $strong),
            'skip_races' => implode(',', $skip),
            'portfolio_confidence' => round($portfolio, 4),
            'portfolio_state' => $state,
            'race_count' => count($races)
        );
    }

    private function snapshotPath($date, $venue)
    {
        $safeVenue = preg_replace('/[^A-Za-z0-9_-]+/', '_', (string)$venue);
        if ($safeVenue === '') {
            $safeVenue = 'ALL';
        }
        return $this->snapshotDir . '/' . preg_replace('/[^0-9-]/', '', (string)$date) . '_' . $safeVenue . '.json';
    }

    private function writeSnapshot(array $payload, $date, $venue)
    {
        if (!is_dir($this->snapshotDir) && !@mkdir($this->snapshotDir, 0775, true) && !is_dir($this->snapshotDir)) {
            return false;
        }
        $json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
        if ($json === false) {
            return false;
        }
        return @file_put_contents($this->snapshotPath($date, $venue), $json, LOCK_EX) !== false;
    }

    public function latest($date, $venue = '')
    {
        if (!$this->tableExists('wr_chairman_ultimate_race_v50')) {
            return array(
                'status' => 'SOURCE_MISSING',
                'message' => 'V50 race table not found. Build/install V50 first.',
                'pre_race_only' => true,
                'results_used' => false,
                'source_v50_run' => '',
                'day' => array(),
                'races' => array()
            );
        }

        $runKey = $this->latestV50Run($date, $venue);
        if ($runKey === '') {
            return array(
                'status' => 'EMPTY',
                'message' => 'No V50 run found for selected date/venue.',
                'pre_race_only' => true,
                'results_used' => false,
                'source_v50_run' => '',
                'day' => array(),
                'races' => array()
            );
        }

        $where = array('race_date=' . $this->q($date), 'run_key=' . $this->q($runKey));
        if ($venue !== '') {
            $where[] = 'venue=' . $this->q($venue);
        }
        $sourceRows = $this->rows(
            'SELECT * FROM wr_chairman_ultimate_race_v50 WHERE ' .
            implode(' AND ', $where) .
            ' ORDER BY race_num'
        );

        $races = array();
        foreach ($sourceRows as $sourceRow) {
            $races[] = $this->calibrateRace($sourceRow);
        }
        $day = $this->buildSummary($races);

        return array(
            'status' => $races ? 'OK' : 'EMPTY',
            'engine' => 'V51',
            'engine_version' => '51.0.0',
            'pre_race_only' => true,
            'results_used' => false,
            'source_v50_run' => $runKey,
            'race_date' => $date,
            'venue' => $venue,
            'day' => $day,
            'races' => $races
        );
    }

    public function build($date, $venue = '')
    {
        $payload = $this->latest($date, $venue);
        $payload['snapshot_written'] = false;
        if ($payload['status'] === 'OK') {
            $payload['snapshot_written'] = $this->writeSnapshot($payload, $date, $venue);
        }
        return $payload;
    }
}
