<?php
/**
 * WinRacing V64.0 — Controlled Go-Live Rehearsal
 * Final pre-production rehearsal over the V63 certified shadow payload.
 * Read-only: no DB writes, no schema changes, no score or live-weight mutation.
 */
final class ChairmanGoLiveRehearsalV64
{
    public function rehearse(array $source, array $os, array $leakage, array $readiness, array $shadow, $raceDate = '', $venue = '')
    {
        $checks = array();
        $findings = array();
        $shadowSummary = isset($shadow['shadow_summary']) && is_array($shadow['shadow_summary']) ? $shadow['shadow_summary'] : array();
        $readySummary = isset($readiness['readiness_summary']) && is_array($readiness['readiness_summary']) ? $readiness['readiness_summary'] : array();
        $leakSummary = isset($leakage['leakage_summary']) && is_array($leakage['leakage_summary']) ? $leakage['leakage_summary'] : array();
        $osSummary = isset($os['os_summary']) && is_array($os['os_summary']) ? $os['os_summary'] : array();

        $this->check($checks, $findings, 'V63_SHADOW_CERTIFIED',
            isset($shadowSummary['shadow_state']) && $shadowSummary['shadow_state'] === 'PASS',
            'CRITICAL', 'V63 shadow state is not PASS.');
        $this->check($checks, $findings, 'V63_SHADOW_GATE_READY',
            isset($shadowSummary['shadow_gate']) && $shadowSummary['shadow_gate'] === 'SHADOW_READY',
            'CRITICAL', 'V63 shadow gate is not SHADOW_READY.');
        $this->check($checks, $findings, 'V62_PRODUCTION_READY',
            isset($readySummary['production_gate']) && $readySummary['production_gate'] === 'READY',
            'CRITICAL', 'V62 production gate is not READY.');
        $this->check($checks, $findings, 'V61_LEAKAGE_PASS',
            isset($leakSummary['audit_state']) && $leakSummary['audit_state'] === 'PASS',
            'CRITICAL', 'V61 pre-race leakage audit is not PASS.');

        $sourceFingerprint = $this->fingerprint($source);
        $shadowFingerprint = isset($shadowSummary['source_fingerprint']) ? (string)$shadowSummary['source_fingerprint'] : '';
        $this->check($checks, $findings, 'SOURCE_FINGERPRINT_MATCH',
            $sourceFingerprint !== '' && $shadowFingerprint !== '' && hash_equals($sourceFingerprint, $shadowFingerprint),
            'CRITICAL', 'Source payload changed after V63 shadow certification.');

        $decisionCount = isset($shadowSummary['decision_count']) ? (int)$shadowSummary['decision_count'] : 0;
        $this->check($checks, $findings, 'DECISION_COVERAGE_READY',
            $decisionCount >= 5,
            'CRITICAL', 'Decision coverage is below the launch minimum.');
        $this->check($checks, $findings, 'DETERMINISTIC_REPLAY_READY',
            !empty($shadowSummary['deterministic_replay']),
            'CRITICAL', 'Deterministic replay is not certified.');

        $this->check($checks, $findings, 'OS_HEALTH_READY',
            $this->osReady($osSummary),
            'WARNING', 'Chairman OS health is not at the preferred launch level.');
        $this->check($checks, $findings, 'READ_ONLY_REHEARSAL',
            $this->immutable($source, $os, $leakage, $readiness, $shadow),
            'CRITICAL', 'A prohibited mutation flag was detected during rehearsal.');
        $this->check($checks, $findings, 'RESULTS_ISOLATION',
            !$this->containsTrueFlag($source, array('results_used','result_available')),
            'CRITICAL', 'Result data is exposed in the pre-race launch payload.');
        $this->check($checks, $findings, 'ROLLBACK_PLAN_PRESENT',
            true,
            'CRITICAL', 'Rollback plan is unavailable.');
        $this->check($checks, $findings, 'FAIL_CLOSED_POLICY',
            true,
            'CRITICAL', 'Fail-closed production policy is unavailable.');

        $critical = 0; $warning = 0;
        foreach ($findings as $finding) {
            if ($finding['severity'] === 'CRITICAL') { $critical++; }
            elseif ($finding['severity'] === 'WARNING') { $warning++; }
        }
        $score = max(0, 100 - ($critical * 30) - ($warning * 6));
        $state = $critical > 0 ? 'FAIL' : ($warning > 0 ? 'READY_WITH_REVIEW' : 'PASS');
        $gate = $critical > 0 ? 'BLOCKED' : ($warning > 0 ? 'GO_LIVE_REHEARSAL_REVIEW' : 'GO_LIVE_REHEARSAL_READY');

        return array(
            'status' => 'OK',
            'engine' => 'V64',
            'engine_version' => '64.0.0',
            'race_date' => (string)$raceDate,
            'venue' => (string)$venue,
            'mode' => 'READ_ONLY_GO_LIVE_REHEARSAL',
            'live_weights_changed' => false,
            'database_schema_changed' => false,
            'source_decisions_changed' => false,
            'rehearsal_summary' => array(
                'rehearsal_state' => $state,
                'rehearsal_gate' => $gate,
                'rehearsal_score' => round($score, 1),
                'check_count' => count($checks),
                'passed_count' => count($checks) - count($findings),
                'critical_count' => $critical,
                'warning_count' => $warning,
                'decision_count' => $decisionCount,
                'source_fingerprint' => $sourceFingerprint,
                'fingerprint_match' => $sourceFingerprint !== '' && $shadowFingerprint !== '' && hash_equals($sourceFingerprint, $shadowFingerprint),
                'v63_shadow_state' => isset($shadowSummary['shadow_state']) ? $shadowSummary['shadow_state'] : 'UNKNOWN',
                'v62_production_gate' => isset($readySummary['production_gate']) ? $readySummary['production_gate'] : 'UNKNOWN',
                'v61_audit_state' => isset($leakSummary['audit_state']) ? $leakSummary['audit_state'] : 'UNKNOWN',
                'os_state' => isset($osSummary['os_state']) ? $osSummary['os_state'] : (isset($osSummary['state']) ? $osSummary['state'] : 'UNKNOWN')
            ),
            'checks' => $checks,
            'findings' => $findings,
            'launch_runbook' => array(
                'step_1' => 'Confirm V61 leakage audit PASS',
                'step_2' => 'Confirm V62 production gate READY',
                'step_3' => 'Confirm V63 shadow replay PASS and fingerprint match',
                'step_4' => 'Open production in read-only monitored mode',
                'step_5' => 'Fail closed immediately on any critical drift',
                'rollback_trigger' => 'Any critical check failure, fingerprint drift, result leakage or mutation flag',
                'rollback_action' => 'Return to V63 shadow-only mode and preserve the last certified payload'
            ),
            'rehearsal_contract' => array(
                'pre_race_only' => true,
                'v61_pass_required' => true,
                'v62_ready_required' => true,
                'v63_pass_required' => true,
                'fingerprint_match_required' => true,
                'db_write_permitted' => false,
                'schema_change_permitted' => false,
                'live_weight_change_permitted' => false,
                'production_decisions_changed' => false,
                'fail_closed' => true,
                'final_release_stage_permitted' => $critical === 0
            )
        );
    }

    private function osReady(array $summary)
    {
        $state = isset($summary['os_state']) ? strtoupper((string)$summary['os_state']) : (isset($summary['state']) ? strtoupper((string)$summary['state']) : '');
        return in_array($state, array('OPTIMAL','READY','PASS','OPEN'), true) || (isset($summary['health_score']) && (float)$summary['health_score'] >= 80);
    }

    private function check(array &$checks, array &$findings, $name, $passed, $severity, $message)
    {
        $checks[] = array('check'=>$name,'status'=>$passed?'PASS':'FAIL','severity'=>$severity);
        if (!$passed) { $findings[] = array('type'=>$name,'severity'=>$severity,'message'=>$message); }
    }

    private function containsTrueFlag(array $data, array $keys)
    {
        foreach ($data as $key => $value) {
            $k = is_string($key) ? strtolower($key) : '';
            if (in_array($k, $keys, true) && $value === true) { return true; }
            if (is_array($value) && $this->containsTrueFlag($value, $keys)) { return true; }
        }
        return false;
    }

    private function immutable()
    {
        $payloads = func_get_args();
        foreach ($payloads as $payload) {
            if (!is_array($payload)) { continue; }
            foreach (array('live_weights_changed','database_schema_changed','source_decisions_changed') as $key) {
                if (isset($payload[$key]) && $payload[$key] === true) { return false; }
            }
        }
        return true;
    }

    private function fingerprint(array $payload)
    {
        $copy = $payload;
        $this->ksortRecursive($copy);
        $json = json_encode($copy, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        return is_string($json) ? hash('sha256', $json) : '';
    }

    private function ksortRecursive(&$value)
    {
        if (!is_array($value)) { return; }
        if ($this->isAssoc($value)) { ksort($value); }
        foreach ($value as &$child) { $this->ksortRecursive($child); }
        unset($child);
    }

    private function isAssoc(array $array)
    {
        $i = 0;
        foreach ($array as $key => $unused) { if ($key !== $i++) { return true; } }
        return false;
    }
}
