<?php
/**
 * WinRacing V60.0 — Chairman OS Scheduler + Resource Manager
 * Advisory-only runtime planning layer. No DB writes, no schema changes,
 * no score/weight mutation and no modification of V53/V54 decisions.
 */
final class ChairmanOSSchedulerV60
{
    private $startedAt;

    public function __construct()
    {
        $this->startedAt = microtime(true);
    }

    public function build(array $sourceData, $raceDate = '', $venue = '')
    {
        $memory = isset($sourceData['memory_records']) && is_array($sourceData['memory_records']) ? count($sourceData['memory_records']) : 0;
        $clusters = isset($sourceData['pattern_clusters']) && is_array($sourceData['pattern_clusters']) ? count($sourceData['pattern_clusters']) : 0;
        $edges = isset($sourceData['relationship_graph_edges']) && is_array($sourceData['relationship_graph_edges']) ? count($sourceData['relationship_graph_edges']) : 0;
        $ranked = isset($sourceData['final_chairman_ranking']) && is_array($sourceData['final_chairman_ranking']) ? count($sourceData['final_chairman_ranking']) : 0;
        $dashboard = isset($sourceData['ultimate_dashboard']) && is_array($sourceData['ultimate_dashboard']) ? count($sourceData['ultimate_dashboard']) : 0;

        $jobs = array(
            $this->job('BOOTSTRAP', 'Runtime Bootstrap', 100, 1.0, 18, array(), 'READY'),
            $this->job('SOURCE', 'Source Snapshot Loader', 96, 1.1 + min(2.5, $memory / 20), 55 + $memory * 2, array('BOOTSTRAP'), $memory > 0 ? 'READY' : 'WAIT'),
            $this->job('MEMORY', 'Pattern Memory Engine', 92, 1.8 + min(4, $memory / 12), 85 + $memory * 3, array('SOURCE'), $memory > 0 ? 'READY' : 'WAIT'),
            $this->job('CLUSTER', 'Pattern Cluster Engine', 88, 1.4 + min(3, $clusters / 5), 70 + $clusters * 5, array('MEMORY'), $clusters > 0 ? 'READY' : 'WAIT'),
            $this->job('GRAPH', 'Relationship Graph Engine', 84, 1.5 + min(4, $edges / 12), 90 + $edges * 3, array('CLUSTER'), $edges > 0 ? 'READY' : 'WAIT'),
            $this->job('DECISION', 'Chairman Decision Pipeline', 80, 2.2 + min(3, $ranked / 5), 110 + $ranked * 5, array('GRAPH'), $ranked > 0 ? 'READY' : 'WAIT'),
            $this->job('SAFETY', 'Audit + Safety Governor', 78, 1.6 + min(2.5, $dashboard / 6), 80 + $dashboard * 4, array('DECISION'), $dashboard > 0 ? 'READY' : 'WAIT'),
            $this->job('DASHBOARD', 'Dashboard Composer', 70, 1.2 + min(2, $dashboard / 8), 65 + $dashboard * 3, array('SAFETY'), 'READY'),
            $this->job('TELEMETRY', 'Runtime Telemetry', 60, 0.8, 30, array('BOOTSTRAP'), 'READY')
        );

        usort($jobs, function ($a, $b) { return $b['priority'] <=> $a['priority']; });
        $cpuBudget = 100.0;
        $memoryLimitMb = $this->memoryLimitMb();
        $estimatedMemory = 0.0;
        $estimatedCost = 0.0;
        $ready = 0;
        $waiting = 0;
        foreach ($jobs as $job) {
            $estimatedMemory += $job['estimated_memory_mb'];
            $estimatedCost += $job['estimated_cost_units'];
            if ($job['state'] === 'READY') { $ready++; } else { $waiting++; }
        }

        $memoryPressure = $memoryLimitMb > 0 ? min(100, ($estimatedMemory / $memoryLimitMb) * 100) : 0;
        $cpuPressure = min(100, $estimatedCost / 28 * 100);
        $health = max(0, 100 - ($waiting * 8) - max(0, $memoryPressure - 75) * 0.35 - max(0, $cpuPressure - 85) * 0.25);
        $state = $health >= 90 ? 'OPTIMAL' : ($health >= 75 ? 'STABLE' : ($health >= 55 ? 'CAUTION' : 'GUARDED'));

        $waves = $this->executionWaves($jobs);
        $skip = array();
        foreach ($jobs as $job) {
            if ($job['state'] !== 'READY') {
                $skip[] = array('job_id' => $job['job_id'], 'reason' => 'Dependency/source unavailable', 'action' => 'SMART_SKIP');
            }
        }

        return array(
            'status' => 'OK',
            'engine' => 'V60',
            'engine_version' => '60.0.0',
            'race_date' => (string)$raceDate,
            'venue' => (string)$venue,
            'mode' => 'ADVISORY_ONLY',
            'live_weights_changed' => false,
            'database_schema_changed' => false,
            'source_decisions_changed' => false,
            'os_summary' => array(
                'os_state' => $state,
                'health_score' => round($health, 1),
                'job_count' => count($jobs),
                'ready_count' => $ready,
                'waiting_count' => $waiting,
                'execution_wave_count' => count($waves),
                'estimated_cost_units' => round($estimatedCost, 2),
                'estimated_memory_mb' => round($estimatedMemory, 1),
                'memory_limit_mb' => round($memoryLimitMb, 1),
                'memory_pressure_pct' => round($memoryPressure, 1),
                'cpu_pressure_pct' => round($cpuPressure, 1),
                'parallel_capacity' => 3,
                'smart_skip_count' => count($skip),
                'runtime_ms' => round((microtime(true) - $this->startedAt) * 1000, 3)
            ),
            'priority_queue' => $jobs,
            'dependency_graph' => $this->dependencyGraph($jobs),
            'execution_waves' => $waves,
            'smart_skips' => $skip,
            'resource_governor' => array(
                'cpu_budget_pct' => $cpuBudget,
                'memory_budget_mb' => round($memoryLimitMb * 0.72, 1),
                'max_parallel_jobs' => 3,
                'soft_timeout_seconds' => 900,
                'hard_timeout_seconds' => 1200,
                'governor_state' => $state === 'GUARDED' ? 'CAP' : 'OPEN',
                'policy' => 'READ_ONLY_ADVISORY'
            ),
            'runtime_telemetry' => array(
                'php_version' => PHP_VERSION,
                'peak_memory_mb' => round(memory_get_peak_usage(true) / 1048576, 2),
                'current_memory_mb' => round(memory_get_usage(true) / 1048576, 2),
                'server_time_hkt' => (new DateTimeImmutable('now', new DateTimeZone('Asia/Hong_Kong')))->format('Y-m-d H:i:s'),
                'sapi' => PHP_SAPI
            )
        );
    }

    private function job($id, $name, $priority, $cost, $memory, array $depends, $state)
    {
        return array(
            'job_id' => $id,
            'job_name' => $name,
            'priority' => (int)$priority,
            'estimated_cost_units' => round((float)$cost, 2),
            'estimated_memory_mb' => round((float)$memory, 1),
            'dependencies' => $depends,
            'state' => $state,
            'execution_policy' => $state === 'READY' ? 'RUN' : 'SMART_SKIP'
        );
    }

    private function dependencyGraph(array $jobs)
    {
        $graph = array();
        foreach ($jobs as $job) {
            foreach ($job['dependencies'] as $dependency) {
                $graph[] = array('from' => $dependency, 'to' => $job['job_id'], 'type' => 'REQUIRES');
            }
        }
        return $graph;
    }

    private function executionWaves(array $jobs)
    {
        $remaining = array();
        foreach ($jobs as $job) { $remaining[$job['job_id']] = $job; }
        $done = array();
        $waves = array();
        $waveNo = 1;
        while ($remaining && $waveNo <= 20) {
            $wave = array();
            foreach ($remaining as $id => $job) {
                if ($job['state'] !== 'READY') { continue; }
                $ok = true;
                foreach ($job['dependencies'] as $dependency) {
                    if (!isset($done[$dependency])) { $ok = false; break; }
                }
                if ($ok) { $wave[] = $id; }
                if (count($wave) >= 3) { break; }
            }
            if (!$wave) { break; }
            foreach ($wave as $id) { $done[$id] = true; unset($remaining[$id]); }
            $waves[] = array('wave' => $waveNo, 'jobs' => $wave, 'parallel_jobs' => count($wave));
            $waveNo++;
        }
        return $waves;
    }

    private function memoryLimitMb()
    {
        $value = trim((string)ini_get('memory_limit'));
        if ($value === '' || $value === '-1') { return 1536.0; }
        $unit = strtolower(substr($value, -1));
        $number = (float)$value;
        if ($unit === 'g') { return $number * 1024; }
        if ($unit === 'k') { return $number / 1024; }
        if ($unit === 'm') { return $number; }
        return $number / 1048576;
    }
}
