<?php
class ChairmanPatternMemoryEngineV53
{
    private $db;
    private $v52;
    private $v52SnapshotDir;
    private $snapshotDir;

    public function __construct(mysqli $db, $v52SnapshotDir = '', $snapshotDir = '')
    {
        $this->db = $db;
        require_once dirname(__DIR__) . '/learning/ChairmanSelfLearningEngineV52.php';
        $this->v52SnapshotDir = $v52SnapshotDir !== '' ? rtrim($v52SnapshotDir, '/\\') : dirname(__DIR__, 2) . '/runtime/cache/v52';
        $this->snapshotDir = $snapshotDir !== '' ? rtrim($snapshotDir, '/\\') : dirname(__DIR__, 2) . '/runtime/cache/v53';
        $this->v52 = new ChairmanSelfLearningEngineV52($db, $this->v52SnapshotDir);
    }

    private function clamp($v, $min = 0.0, $max = 100.0) { return max($min, min($max, (float)$v)); }
    private function key($type, $name) { return strtoupper(trim((string)$type)) . '|' . trim((string)$name); }
    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;
    }
    private function loadHistory($date, $venue)
    {
        $items = array();
        if (!is_dir($this->v52SnapshotDir)) { return $items; }
        $files = glob($this->v52SnapshotDir . '/*.json');
        if (!is_array($files)) { return $items; }
        sort($files, SORT_STRING);
        foreach ($files as $file) {
            $raw = @file_get_contents($file);
            $p = $raw !== false ? json_decode($raw, true) : null;
            if (!is_array($p) || !isset($p['race_date'])) { continue; }
            $d = (string)$p['race_date'];
            $v = isset($p['venue']) ? (string)$p['venue'] : '';
            if ($d > $date) { continue; }
            if ($venue !== '' && $v !== '' && $v !== $venue) { continue; }
            $items[] = $p;
        }
        return $items;
    }
    private function addRecord(array &$map, $type, $name, $date, $sample, $score, $outcome, $recommendation, $source)
    {
        $name = trim((string)$name);
        if ($name === '' || $name === 'NONE') { return; }
        $k = $this->key($type, $name);
        if (!isset($map[$k])) {
            $map[$k] = array('memory_key'=>$k,'memory_type'=>strtoupper((string)$type),'memory_name'=>$name,'sample_size'=>0,'weighted_score_sum'=>0.0,'weight_sum'=>0.0,'positive_votes'=>0,'negative_votes'=>0,'neutral_votes'=>0,'first_seen'=>$date,'last_seen'=>$date,'source_versions'=>array(),'recommendations'=>array());
        }
        $w = max(1.0, (float)$sample);
        $map[$k]['sample_size'] += max(1, (int)$sample);
        $map[$k]['weighted_score_sum'] += ((float)$score) * $w;
        $map[$k]['weight_sum'] += $w;
        if ($outcome > 0) { $map[$k]['positive_votes']++; }
        elseif ($outcome < 0) { $map[$k]['negative_votes']++; }
        else { $map[$k]['neutral_votes']++; }
        if ($date < $map[$k]['first_seen']) { $map[$k]['first_seen'] = $date; }
        if ($date > $map[$k]['last_seen']) { $map[$k]['last_seen'] = $date; }
        $map[$k]['source_versions'][$source] = true;
        $recommendation = strtoupper(trim((string)$recommendation));
        if ($recommendation !== '') { $map[$k]['recommendations'][$recommendation] = isset($map[$k]['recommendations'][$recommendation]) ? $map[$k]['recommendations'][$recommendation] + 1 : 1; }
    }
    private function absorb(array &$map, array $p)
    {
        $date = isset($p['race_date']) ? (string)$p['race_date'] : '';
        $version = isset($p['engine_version']) ? 'V52.' . (string)$p['engine_version'] : 'V52';
        $patterns = isset($p['patterns']) && is_array($p['patterns']) ? $p['patterns'] : array();
        foreach ($patterns as $r) {
            $name = isset($r['pattern_key']) ? $r['pattern_key'] : (isset($r['pattern']) ? $r['pattern'] : '');
            $sample = isset($r['sample_size']) ? $r['sample_size'] : (isset($r['evaluated']) ? $r['evaluated'] : 1);
            $score = isset($r['pattern_confidence']) ? $r['pattern_confidence'] : (isset($r['posterior_hit_rate_pct']) ? $r['posterior_hit_rate_pct'] : 50);
            $rec = isset($r['pattern_recommendation']) ? $r['pattern_recommendation'] : (isset($r['recommendation']) ? $r['recommendation'] : 'HOLD');
            $vote = in_array(strtoupper((string)$rec), array('PROMOTE','REMEMBER'), true) ? 1 : (in_array(strtoupper((string)$rec), array('DOWNGRADE','RETIRE','AVOID'), true) ? -1 : 0);
            $this->addRecord($map,'PATTERN',$name,$date,$sample,$score,$vote,$rec,$version);
        }
        $dna = isset($p['race_dna_learning']['profiles']) && is_array($p['race_dna_learning']['profiles']) ? $p['race_dna_learning']['profiles'] : array();
        foreach ($dna as $r) {
            $name = isset($r['dna_signature']) ? $r['dna_signature'] : '';
            $sample = isset($r['sample_size']) ? $r['sample_size'] : (isset($r['evaluated']) ? $r['evaluated'] : 1);
            $score = isset($r['dna_confidence']) ? $r['dna_confidence'] : (isset($r['posterior_top3_rate_pct']) ? $r['posterior_top3_rate_pct'] : 50);
            $rec = isset($r['dna_recommendation']) ? $r['dna_recommendation'] : 'HOLD';
            $vote = $rec === 'PROMOTE' ? 1 : ($rec === 'AVOID' ? -1 : 0);
            $this->addRecord($map,'DNA',$name,$date,$sample,$score,$vote,$rec,$version);
        }
        $causes = isset($p['error_analysis']['root_causes']) && is_array($p['error_analysis']['root_causes']) ? $p['error_analysis']['root_causes'] : array();
        foreach ($causes as $r) {
            $name = isset($r['root_cause']) ? $r['root_cause'] : '';
            $sample = isset($r['count']) ? $r['count'] : 1;
            $score = isset($r['priority_score']) ? $r['priority_score'] : (isset($r['average_severity']) ? $r['average_severity'] : 50);
            $this->addRecord($map,'ERROR',$name,$date,$sample,$score,-1,'GUARD',$version);
        }
        $candidates = isset($p['auto_pattern_discovery']['candidates']) && is_array($p['auto_pattern_discovery']['candidates']) ? $p['auto_pattern_discovery']['candidates'] : array();
        foreach ($candidates as $r) {
            $name = isset($r['pattern']) ? $r['pattern'] : '';
            $sample = isset($r['evaluated']) ? $r['evaluated'] : 1;
            $score = isset($r['discovery_confidence']) ? $r['discovery_confidence'] : 50;
            $rec = isset($r['candidate_status']) ? $r['candidate_status'] : 'WATCH';
            $vote = $rec === 'PROMOTE' ? 1 : ($rec === 'REJECT' ? -1 : 0);
            $this->addRecord($map,'DISCOVERY',$name,$date,$sample,$score,$vote,$rec,$version);
        }
    }
    private function finalize(array $map, $date)
    {
        $records = array();
        foreach ($map as $r) {
            $avg = $r['weight_sum'] > 0 ? $r['weighted_score_sum'] / $r['weight_sum'] : 0.0;
            $totalVotes = $r['positive_votes'] + $r['negative_votes'] + $r['neutral_votes'];
            $voteBalance = $totalVotes > 0 ? (($r['positive_votes'] - $r['negative_votes']) / $totalVotes) * 100.0 : 0.0;
            $ageDays = max(0, (int)floor((strtotime($date) - strtotime($r['last_seen'])) / 86400));
            $recency = max(15.0, 100.0 - min(85.0, $ageDays * 4.0));
            $sampleStrength = min(100.0, log(1.0 + max(1, $r['sample_size'])) / log(41.0) * 100.0);
            $conflict = $totalVotes > 0 ? (min($r['positive_votes'],$r['negative_votes']) / $totalVotes) * 100.0 : 0.0;
            $strength = $this->clamp($avg * 0.38 + $sampleStrength * 0.27 + $recency * 0.20 + max(0.0,$voteBalance) * 0.15 - $conflict * 0.20);
            $sentiment = $voteBalance >= 20 ? 'POSITIVE' : ($voteBalance <= -20 ? 'NEGATIVE' : 'NEUTRAL');
            if ($r['memory_type'] === 'ERROR') { $sentiment = 'NEGATIVE'; }
            if ($sentiment === 'POSITIVE' && $strength >= 65) { $recommendation = 'PROMOTE'; }
            elseif ($sentiment === 'NEGATIVE' && $strength >= 58) { $recommendation = 'GUARD'; }
            elseif ($conflict >= 30) { $recommendation = 'CAUTION'; }
            elseif ($strength >= 45) { $recommendation = 'REMEMBER'; }
            else { $recommendation = 'HOLD'; }
            arsort($r['recommendations']);
            $records[] = array(
                'memory_key'=>$r['memory_key'],'memory_type'=>$r['memory_type'],'memory_name'=>$r['memory_name'],
                'sample_size'=>$r['sample_size'],'memory_strength'=>round($strength,2),'average_source_score'=>round($avg,2),
                'recency_score'=>round($recency,2),'sample_strength'=>round($sampleStrength,2),'vote_balance'=>round($voteBalance,2),
                'conflict_pct'=>round($conflict,2),'sentiment'=>$sentiment,'recommendation'=>$recommendation,
                'positive_votes'=>$r['positive_votes'],'negative_votes'=>$r['negative_votes'],'neutral_votes'=>$r['neutral_votes'],
                'first_seen'=>$r['first_seen'],'last_seen'=>$r['last_seen'],'source_versions'=>array_keys($r['source_versions']),
                'dominant_source_recommendation'=>key($r['recommendations']) !== null ? key($r['recommendations']) : 'NONE'
            );
        }
        usort($records, function($a,$b){ if ($a['memory_strength']===$b['memory_strength']) return strcmp($a['memory_key'],$b['memory_key']); return $a['memory_strength']<$b['memory_strength']?1:-1; });
        return $records;
    }

    private function clusterTokens($name)
    {
        $raw = strtoupper((string)$name);
        $parts = preg_split('/[^A-Z0-9]+/', $raw, -1, PREG_SPLIT_NO_EMPTY);
        $stop = array('PATTERN','DNA','ERROR','DISCOVERY','TIER','ACTION','SCORE','RISK','CONS','REL','MID','LOW','HIGH');
        $tokens = array();
        foreach ($parts as $p) {
            if (strlen($p) < 2 || in_array($p, $stop, true)) { continue; }
            $tokens[$p] = true;
        }
        foreach (array('WATCH','SKIP','PRIMARY','SECONDARY','CAUTION','OVERCONFIDENCE','PENALTY','THRESHOLD','BALANCED','VOLATILE','DANGER','PROMOTE','GUARD') as $tag) {
            if (strpos($raw, $tag) !== false) { $tokens[$tag] = true; }
        }
        return array_keys($tokens);
    }
    private function jaccard(array $a, array $b)
    {
        if (!$a && !$b) { return 1.0; }
        $aa = array_fill_keys($a, true); $bb = array_fill_keys($b, true);
        $inter = count(array_intersect_key($aa, $bb));
        $union = count($aa + $bb);
        return $union > 0 ? $inter / $union : 0.0;
    }
    private function buildClusters(array $records)
    {
        $clusters = array();
        foreach ($records as $record) {
            $tokens = $this->clusterTokens($record['memory_name']);
            $best = -1; $bestScore = 0.0;
            foreach ($clusters as $i => $cluster) {
                $typeBonus = $cluster['memory_type'] === $record['memory_type'] ? 0.12 : 0.0;
                $score = $this->jaccard($tokens, $cluster['token_union']) + $typeBonus;
                if ($score > $bestScore) { $bestScore = $score; $best = $i; }
            }
            $threshold = $record['memory_type'] === 'ERROR' ? 0.32 : 0.42;
            if ($best >= 0 && $bestScore >= $threshold) {
                $clusters[$best]['members'][] = $record;
                $clusters[$best]['token_union'] = array_values(array_unique(array_merge($clusters[$best]['token_union'], $tokens)));
            } else {
                $clusters[] = array('memory_type'=>$record['memory_type'],'token_union'=>$tokens,'members'=>array($record));
            }
        }
        $out = array(); $id = 1; $mapping = array();
        foreach ($clusters as $cluster) {
            $members = $cluster['members'];
            usort($members, function($a,$b){ return $a['memory_strength'] < $b['memory_strength'] ? 1 : ($a['memory_strength'] > $b['memory_strength'] ? -1 : 0); });
            $size = count($members); $strengths = array_column($members,'memory_strength');
            $avg = $size ? array_sum($strengths)/$size : 0.0;
            $variance = 0.0; foreach ($strengths as $v) { $variance += ($v-$avg)*($v-$avg); }
            $std = $size ? sqrt($variance/$size) : 0.0;
            $cohesion = $this->clamp(100.0 - $std*3.2 - max(0,$size-1)*1.5);
            $pos=$neg=$neu=0; $conflictSum=0.0; $sample=0;
            foreach ($members as $m) {
                if ($m['sentiment']==='POSITIVE') $pos++; elseif ($m['sentiment']==='NEGATIVE') $neg++; else $neu++;
                $conflictSum += (float)$m['conflict_pct']; $sample += (int)$m['sample_size'];
            }
            $dominant = $pos>$neg && $pos>=$neu ? 'POSITIVE' : ($neg>$pos && $neg>=$neu ? 'NEGATIVE' : 'NEUTRAL');
            $conflict = $size ? ($conflictSum/$size + (min($pos,$neg)/max(1,$size))*50.0) : 0.0;
            $clusterStrength = $this->clamp($avg*0.58 + $cohesion*0.22 + min(100,log(1+$sample)/log(61)*100)*0.20 - $conflict*0.18);
            if ($dominant==='POSITIVE' && $clusterStrength>=65 && $cohesion>=62) $rec='PROMOTE';
            elseif ($dominant==='NEGATIVE' && $clusterStrength>=55) $rec='GUARD';
            elseif ($conflict>=28 || $cohesion<45) $rec='CAUTION';
            elseif ($clusterStrength>=48) $rec='REMEMBER'; else $rec='HOLD';
            $clusterId='CL'.str_pad((string)$id,3,'0',STR_PAD_LEFT);
            $label=$cluster['memory_type'].' · '.implode('+',array_slice($cluster['token_union'],0,3));
            if (trim($label," ·+")===$cluster['memory_type']) $label=$cluster['memory_type'].' · '.$members[0]['memory_name'];
            foreach ($members as $m) $mapping[$m['memory_key']]=$clusterId;
            $out[] = array(
                'cluster_id'=>$clusterId,'cluster_label'=>$label,'memory_type'=>$cluster['memory_type'],
                'cluster_size'=>$size,'total_samples'=>$sample,'cluster_strength'=>round($clusterStrength,2),
                'cohesion_score'=>round($cohesion,2),'conflict_pct'=>round($conflict,2),
                'dominant_sentiment'=>$dominant,'recommendation'=>$rec,
                'representative_pattern'=>$members[0]['memory_key'],'member_keys'=>array_column($members,'memory_key'),
                'positive_count'=>$pos,'negative_count'=>$neg,'neutral_count'=>$neu
            );
            $id++;
        }
        usort($out,function($a,$b){ return $a['cluster_strength']<$b['cluster_strength']?1:($a['cluster_strength']>$b['cluster_strength']?-1:strcmp($a['cluster_id'],$b['cluster_id'])); });
        $promote=$guard=$caution=0; foreach($out as $c){if($c['recommendation']==='PROMOTE')$promote++;elseif($c['recommendation']==='GUARD')$guard++;elseif($c['recommendation']==='CAUTION')$caution++;}
        $avgCohesion=$out?array_sum(array_column($out,'cohesion_score'))/count($out):0.0;
        return array('summary'=>array('cluster_count'=>count($out),'mapped_memory_count'=>count($mapping),'average_cohesion'=>round($avgCohesion,2),'promote_cluster_count'=>$promote,'guard_cluster_count'=>$guard,'caution_cluster_count'=>$caution,'top_cluster'=>isset($out[0])?$out[0]['cluster_id']:'NONE'),'clusters'=>$out,'mapping'=>$mapping);
    }


    private function relationshipTokens($label)
    {
        $parts = preg_split('/[^A-Z0-9]+/', strtoupper((string)$label), -1, PREG_SPLIT_NO_EMPTY);
        $stop = array('CL','PATTERN','DNA','ERROR','DISCOVERY','TIER','ACTION','SCORE','RISK','CONS','REL','MID','LOW','HIGH');
        $tokens = array();
        foreach ($parts as $part) {
            if (strlen($part) < 2 || in_array($part, $stop, true)) { continue; }
            $tokens[$part] = true;
        }
        return array_keys($tokens);
    }
    private function buildRelationshipGraph(array $clusters, array $records, array $mapping)
    {
        $nodes = array();
        $clusterById = array();
        foreach ($clusters as $cluster) {
            $id = (string)$cluster['cluster_id'];
            $clusterById[$id] = $cluster;
            $nodes[] = array(
                'node_id'=>$id,
                'node_type'=>'CLUSTER',
                'label'=>$cluster['cluster_label'],
                'memory_type'=>$cluster['memory_type'],
                'strength'=>(float)$cluster['cluster_strength'],
                'sentiment'=>$cluster['dominant_sentiment'],
                'recommendation'=>$cluster['recommendation'],
                'sample_size'=>(int)$cluster['total_samples'],
                'member_count'=>(int)$cluster['cluster_size'],
                'conflict_pct'=>(float)$cluster['conflict_pct']
            );
        }
        $edges = array();
        $count = count($clusters);
        for ($i=0; $i<$count; $i++) {
            for ($j=$i+1; $j<$count; $j++) {
                $a = $clusters[$i]; $b = $clusters[$j];
                $ta = $this->relationshipTokens($a['cluster_label'].' '.implode(' ', $a['member_keys']));
                $tb = $this->relationshipTokens($b['cluster_label'].' '.implode(' ', $b['member_keys']));
                $similarity = $this->jaccard($ta, $tb) * 100.0;
                $strengthProximity = max(0.0, 100.0 - abs((float)$a['cluster_strength']-(float)$b['cluster_strength'])*4.0);
                $cohesion = ((float)$a['cohesion_score']+(float)$b['cohesion_score'])/2.0;
                $sameType = $a['memory_type']===$b['memory_type'];
                $sameSentiment = $a['dominant_sentiment']===$b['dominant_sentiment'];
                $opposite = ($a['dominant_sentiment']==='POSITIVE' && $b['dominant_sentiment']==='NEGATIVE') || ($a['dominant_sentiment']==='NEGATIVE' && $b['dominant_sentiment']==='POSITIVE');
                $recConflict = ($a['recommendation']==='PROMOTE' && $b['recommendation']==='GUARD') || ($a['recommendation']==='GUARD' && $b['recommendation']==='PROMOTE');
                $relation = 'SUPPORT';
                if ($opposite || $recConflict) { $relation='CONFLICT'; }
                elseif ($a['dominant_sentiment']==='NEGATIVE' && $b['dominant_sentiment']==='NEGATIVE') { $relation='NEGATIVE'; }
                elseif ($sameSentiment && $a['dominant_sentiment']==='POSITIVE') { $relation='POSITIVE'; }
                $sentimentFactor = $opposite ? 3.0 : ($sameSentiment ? 12.0 : 6.0);
                $typeFactor = $sameType ? 9.0 : 3.0;
                $relationshipStrength = $similarity*0.42 + $strengthProximity*0.20 + $cohesion*0.14 + $sentimentFactor + $typeFactor;
                if ($relation==='CONFLICT') { $relationshipStrength += min(14.0, ((float)$a['conflict_pct']+(float)$b['conflict_pct'])/8.0); }
                $relationshipStrength = $this->clamp($relationshipStrength);
                if ($relationshipStrength < 28.0) { continue; }
                $source = (float)$a['cluster_strength'] >= (float)$b['cluster_strength'] ? $a['cluster_id'] : $b['cluster_id'];
                $target = $source===$a['cluster_id'] ? $b['cluster_id'] : $a['cluster_id'];
                $direction = abs((float)$a['cluster_strength']-(float)$b['cluster_strength']) < 3.0 ? 'BIDIRECTIONAL' : 'STRONG_TO_WEAK';
                $edges[] = array(
                    'edge_id'=>'E'.str_pad((string)(count($edges)+1),3,'0',STR_PAD_LEFT),
                    'source'=>$source,'target'=>$target,
                    'relationship_type'=>$relation,
                    'relationship_strength'=>round($relationshipStrength,2),
                    'token_similarity'=>round($similarity,2),
                    'direction'=>$direction,
                    'same_memory_type'=>$sameType,
                    'support_samples'=>(int)$a['total_samples']+(int)$b['total_samples'],
                    'reason'=>$relation.' | similarity '.round($similarity,1).'% | cohesion '.round($cohesion,1)
                );
            }
        }
        usort($edges, function($a,$b){ return $a['relationship_strength']<$b['relationship_strength']?1:($a['relationship_strength']>$b['relationship_strength']?-1:strcmp($a['edge_id'],$b['edge_id'])); });
        $edges = array_slice($edges,0,60);
        $stats = array();
        foreach ($nodes as $node) { $stats[$node['node_id']] = array('degree'=>0,'weighted'=>0.0,'conflict'=>0,'cross_type'=>0); }
        foreach ($edges as $edge) {
            foreach (array($edge['source'],$edge['target']) as $id) {
                if (!isset($stats[$id])) { continue; }
                $stats[$id]['degree']++;
                $stats[$id]['weighted'] += (float)$edge['relationship_strength'];
                if ($edge['relationship_type']==='CONFLICT') { $stats[$id]['conflict']++; }
            }
            if (isset($clusterById[$edge['source']],$clusterById[$edge['target']]) && $clusterById[$edge['source']]['memory_type']!==$clusterById[$edge['target']]['memory_type']) {
                $stats[$edge['source']]['cross_type']++; $stats[$edge['target']]['cross_type']++;
            }
        }
        $nodeMetrics = array();
        foreach ($nodes as $node) {
            $st = $stats[$node['node_id']];
            $centrality = $st['degree']>0 ? min(100.0, ($st['weighted']/max(1,$st['degree']))*0.62 + min(40,$st['degree']*7.0)) : 0.0;
            $bridge = min(100.0, $st['cross_type']*22.0 + $st['degree']*5.0);
            $risk = $this->clamp((float)$node['conflict_pct']*0.65 + $st['conflict']*18.0 + ($node['sentiment']==='NEGATIVE'?18.0:0.0));
            $role = 'PERIPHERAL';
            if ($risk>=55) { $role='RISK_NODE'; }
            elseif ($bridge>=55) { $role='BRIDGE'; }
            elseif ($centrality>=55) { $role='HUB'; }
            $nodeMetrics[] = array('node_id'=>$node['node_id'],'degree'=>$st['degree'],'weighted_degree'=>round($st['weighted'],2),'centrality_score'=>round($centrality,2),'bridge_score'=>round($bridge,2),'risk_score'=>round($risk,2),'node_role'=>$role);
        }
        usort($nodeMetrics,function($a,$b){return $a['centrality_score']<$b['centrality_score']?1:($a['centrality_score']>$b['centrality_score']?-1:strcmp($a['node_id'],$b['node_id']));});
        $hub='NONE';$bridge='NONE';$risk='NONE';
        foreach($nodeMetrics as $m){if($hub==='NONE'&&$m['node_role']==='HUB')$hub=$m['node_id'];if($bridge==='NONE'&&$m['node_role']==='BRIDGE')$bridge=$m['node_id'];}
        $riskSorted=$nodeMetrics;usort($riskSorted,function($a,$b){return $a['risk_score']<$b['risk_score']?1:($a['risk_score']>$b['risk_score']?-1:0);});if(isset($riskSorted[0])&&$riskSorted[0]['risk_score']>0)$risk=$riskSorted[0]['node_id'];
        $positive=$negative=$conflict=$support=0;
        foreach($edges as $e){if($e['relationship_type']==='POSITIVE')$positive++;elseif($e['relationship_type']==='NEGATIVE')$negative++;elseif($e['relationship_type']==='CONFLICT')$conflict++;else$support++;}
        $density = $count>1 ? count($edges)/(($count*($count-1))/2)*100.0 : 0.0;
        return array(
            'summary'=>array('node_count'=>count($nodes),'edge_count'=>count($edges),'graph_density_pct'=>round($density,2),'positive_edge_count'=>$positive,'negative_edge_count'=>$negative,'conflict_edge_count'=>$conflict,'support_edge_count'=>$support,'hub_node'=>$hub,'bridge_node'=>$bridge,'risk_node'=>$risk,'top_relationship'=>isset($edges[0])?$edges[0]['edge_id']:'NONE'),
            'nodes'=>$nodes,'edges'=>$edges,'node_metrics'=>$nodeMetrics
        );
    }


    private function buildExperienceEngine(array $records, array $clusters, array $graphMetrics, array $mapping)
    {
        $metricMap = array();
        foreach ($graphMetrics as $m) { $metricMap[(string)$m['node_id']] = $m; }
        $experiences = array();
        foreach ($clusters as $cluster) {
            $clusterId = (string)$cluster['cluster_id'];
            $metric = isset($metricMap[$clusterId]) ? $metricMap[$clusterId] : array();
            $centrality = isset($metric['centrality_score']) ? (float)$metric['centrality_score'] : 0.0;
            $bridge = isset($metric['bridge_score']) ? (float)$metric['bridge_score'] : 0.0;
            $graphRisk = isset($metric['risk_score']) ? (float)$metric['risk_score'] : 0.0;
            $strength = (float)$cluster['cluster_strength'];
            $cohesion = (float)$cluster['cohesion_score'];
            $conflict = (float)$cluster['conflict_pct'];
            $sampleScore = min(100.0, log(1.0 + max(1, (int)$cluster['total_samples'])) / log(61.0) * 100.0);
            $experienceScore = $this->clamp($strength*0.32 + $cohesion*0.20 + $sampleScore*0.18 + $centrality*0.12 + (100.0-$conflict)*0.10 + (100.0-$graphRisk)*0.08);
            $confidence = $this->clamp($sampleScore*0.35 + $cohesion*0.30 + (100.0-$conflict)*0.20 + min(100.0,$centrality+20.0)*0.15);
            $sentiment = (string)$cluster['dominant_sentiment'];
            if ($sentiment==='POSITIVE' && $experienceScore>=68 && $confidence>=60) { $verdict='TRUST'; }
            elseif ($sentiment==='NEGATIVE' && ($experienceScore>=58 || $graphRisk>=50)) { $verdict='AVOID'; }
            elseif ($conflict>=30 || $graphRisk>=55) { $verdict='CAUTION'; }
            elseif ($experienceScore>=52) { $verdict='REMEMBER'; }
            else { $verdict='OBSERVE'; }
            $lesson = 'Cluster '.$clusterId.' '.$sentiment.' memory; strength '.round($strength,1).', cohesion '.round($cohesion,1).', conflict '.round($conflict,1).'%.';
            if ($verdict==='TRUST') { $lesson .= ' Historical evidence is coherent enough to support future matching contexts.'; }
            elseif ($verdict==='AVOID') { $lesson .= ' Negative historical evidence should act as a protective guard.'; }
            elseif ($verdict==='CAUTION') { $lesson .= ' Mixed or risky evidence requires conservative handling.'; }
            else { $lesson .= ' Keep as advisory experience until more samples arrive.'; }
            $experiences[] = array(
                'experience_id'=>'EXP-'.$clusterId,'cluster_id'=>$clusterId,'cluster_label'=>$cluster['cluster_label'],
                'experience_score'=>round($experienceScore,2),'experience_confidence'=>round($confidence,2),
                'verdict'=>$verdict,'dominant_sentiment'=>$sentiment,'graph_role'=>isset($metric['node_role'])?$metric['node_role']:'PERIPHERAL',
                'centrality_score'=>round($centrality,2),'graph_risk_score'=>round($graphRisk,2),
                'sample_size'=>(int)$cluster['total_samples'],'lesson'=>$lesson,
                'representative_pattern'=>$cluster['representative_pattern']
            );
        }
        usort($experiences,function($a,$b){return $a['experience_score']<$b['experience_score']?1:($a['experience_score']>$b['experience_score']?-1:strcmp($a['experience_id'],$b['experience_id']));});
        $trust=$avoid=$caution=$remember=$observe=0;
        foreach($experiences as $e){if($e['verdict']==='TRUST')$trust++;elseif($e['verdict']==='AVOID')$avoid++;elseif($e['verdict']==='CAUTION')$caution++;elseif($e['verdict']==='REMEMBER')$remember++;else$observe++;}
        $avg=$experiences?array_sum(array_column($experiences,'experience_score'))/count($experiences):0.0;
        return array('summary'=>array('experience_count'=>count($experiences),'average_experience_score'=>round($avg,2),'trust_count'=>$trust,'avoid_count'=>$avoid,'caution_count'=>$caution,'remember_count'=>$remember,'observe_count'=>$observe,'top_experience'=>isset($experiences[0])?$experiences[0]['experience_id']:'NONE'),'experiences'=>$experiences);
    }

    private function buildContextAwareness(array $experiences, array $clusters, array $records)
    {
        $clusterMap=array(); foreach($clusters as $c){$clusterMap[(string)$c['cluster_id']]=$c;}
        $contexts=array();
        foreach($experiences as $e){
            $c=isset($clusterMap[$e['cluster_id']])?$clusterMap[$e['cluster_id']]:array();
            $strength=(float)$e['experience_score']; $confidence=(float)$e['experience_confidence'];
            $risk=(float)$e['graph_risk_score']; $sentiment=(string)$e['dominant_sentiment'];
            $strengthBand=$strength>=68?'STRONG':($strength>=52?'MID':'WEAK');
            $riskBand=$risk>=60?'HIGH':($risk>=30?'MID':'LOW');
            $sampleBand=(int)$e['sample_size']>=20?'MATURE':((int)$e['sample_size']>=6?'DEVELOPING':'EARLY');
            $contextKey='CTX|'.$sentiment.'|'.$strengthBand.'|'.$riskBand.'|'.$sampleBand.'|'.$e['graph_role'];
            if($e['verdict']==='TRUST' && $riskBand!=='HIGH'){$action='SUPPORT';}
            elseif($e['verdict']==='AVOID' || $riskBand==='HIGH'){$action='BLOCK';}
            elseif($e['verdict']==='CAUTION' || $sampleBand==='EARLY'){$action='REDUCE';}
            else{$action='MONITOR';}
            $fit=$this->clamp($confidence*0.42 + $strength*0.28 + (100.0-$risk)*0.18 + ($sampleBand==='MATURE'?12.0:($sampleBand==='DEVELOPING'?6.0:0.0)));
            $contexts[]=array('context_key'=>$contextKey,'experience_id'=>$e['experience_id'],'cluster_id'=>$e['cluster_id'],'context_fit_score'=>round($fit,2),'context_action'=>$action,'sentiment'=>$sentiment,'strength_band'=>$strengthBand,'risk_band'=>$riskBand,'sample_band'=>$sampleBand,'graph_role'=>$e['graph_role'],'context_reason'=>'Verdict '.$e['verdict'].' under '.$strengthBand.' strength / '.$riskBand.' risk / '.$sampleBand.' sample context.');
        }
        usort($contexts,function($a,$b){return $a['context_fit_score']<$b['context_fit_score']?1:($a['context_fit_score']>$b['context_fit_score']?-1:strcmp($a['context_key'],$b['context_key']));});
        $support=$block=$reduce=$monitor=0;foreach($contexts as $x){if($x['context_action']==='SUPPORT')$support++;elseif($x['context_action']==='BLOCK')$block++;elseif($x['context_action']==='REDUCE')$reduce++;else$monitor++;}
        return array('summary'=>array('context_count'=>count($contexts),'support_count'=>$support,'block_count'=>$block,'reduce_count'=>$reduce,'monitor_count'=>$monitor,'top_context'=>isset($contexts[0])?$contexts[0]['context_key']:'NONE'),'contexts'=>$contexts);
    }

    private function buildPatternEvolution(array $records, array $mapping, array $contexts, $date, $historyCount)
    {
        $contextMap=array(); foreach($contexts as $x){$contextMap[(string)$x['cluster_id']]=$x;}
        $evolution=array();
        foreach($records as $r){
            $clusterId=isset($mapping[$r['memory_key']])?$mapping[$r['memory_key']]:'NONE';
            $ctx=isset($contextMap[$clusterId])?$contextMap[$clusterId]:array();
            $age=max(0,(int)floor((strtotime($date)-strtotime($r['first_seen']))/86400));
            $stale=max(0,(int)floor((strtotime($date)-strtotime($r['last_seen']))/86400));
            $sample=(int)$r['sample_size']; $strength=(float)$r['memory_strength']; $conflict=(float)$r['conflict_pct'];
            $momentum=$this->clamp(($r['vote_balance']+100.0)/2.0 + ($r['recency_score']-50.0)*0.35 - $conflict*0.35);
            $maturity=$this->clamp(min(100.0,log(1+max(1,$sample))/log(61)*100.0)*0.55 + min(100.0,$historyCount*12.0)*0.25 + min(100.0,$age*3.0)*0.20);
            if($stale>=28 || ($r['sentiment']==='NEGATIVE' && $strength>=58 && $conflict<20)){$state='RETIRE_CANDIDATE';}
            elseif($sample<=3 && $age<=14){$state='EMERGING';}
            elseif($momentum>=62 && $strength>=52){$state='GROWING';}
            elseif($maturity>=60 && $conflict<25 && $stale<14){$state='STABLE';}
            elseif($momentum<42 || $conflict>=35 || $stale>=14){$state='DECLINING';}
            else{$state='DEVELOPING';}
            if($state==='GROWING')$rec='PROMOTE_CANDIDATE';elseif($state==='STABLE')$rec='KEEP';elseif($state==='DECLINING')$rec='DOWNGRADE';elseif($state==='RETIRE_CANDIDATE')$rec='RETIRE_REVIEW';else$rec='WATCH';
            $evoScore=$this->clamp($strength*0.35+$momentum*0.25+$maturity*0.20+$r['recency_score']*0.20-$conflict*0.20);
            $evolution[]=array('memory_key'=>$r['memory_key'],'memory_name'=>$r['memory_name'],'cluster_id'=>$clusterId,'evolution_state'=>$state,'evolution_score'=>round($evoScore,2),'momentum_score'=>round($momentum,2),'maturity_score'=>round($maturity,2),'age_days'=>$age,'stale_days'=>$stale,'sample_size'=>$sample,'recommendation'=>$rec,'context_action'=>isset($ctx['context_action'])?$ctx['context_action']:'MONITOR','reason'=>'Strength '.round($strength,1).' | momentum '.round($momentum,1).' | maturity '.round($maturity,1).' | conflict '.round($conflict,1).'%');
        }
        usort($evolution,function($a,$b){return $a['evolution_score']<$b['evolution_score']?1:($a['evolution_score']>$b['evolution_score']?-1:strcmp($a['memory_key'],$b['memory_key']));});
        $counts=array('EMERGING'=>0,'DEVELOPING'=>0,'GROWING'=>0,'STABLE'=>0,'DECLINING'=>0,'RETIRE_CANDIDATE'=>0);foreach($evolution as $e){$counts[$e['evolution_state']]++;}
        return array('summary'=>array('pattern_count'=>count($evolution),'emerging_count'=>$counts['EMERGING'],'developing_count'=>$counts['DEVELOPING'],'growing_count'=>$counts['GROWING'],'stable_count'=>$counts['STABLE'],'declining_count'=>$counts['DECLINING'],'retire_candidate_count'=>$counts['RETIRE_CANDIDATE'],'top_evolving_pattern'=>isset($evolution[0])?$evolution[0]['memory_key']:'NONE'),'patterns'=>$evolution);
    }


    private function loadV53History($date, $venue)
    {
        $items = array();
        if (!is_dir($this->snapshotDir)) { return $items; }
        $files = glob($this->snapshotDir . '/*.json');
        if (!is_array($files)) { return $items; }
        sort($files, SORT_STRING);
        foreach ($files as $file) {
            $raw = @file_get_contents($file);
            $payload = $raw !== false ? json_decode($raw, true) : null;
            if (!is_array($payload) || !isset($payload['race_date'])) { continue; }
            $d = (string)$payload['race_date'];
            $v = isset($payload['venue']) ? (string)$payload['venue'] : '';
            if ($d > $date) { continue; }
            if ($venue !== '' && $v !== '' && $v !== $venue) { continue; }
            $items[] = $payload;
        }
        return $items;
    }

    private function buildMultiRaceFusion(array $currentRecords, array $v53History, $date)
    {
        $map = array();
        foreach ($v53History as $snapshot) {
            $snapshotDate = isset($snapshot['race_date']) ? (string)$snapshot['race_date'] : $date;
            $rows = isset($snapshot['memory_records']) && is_array($snapshot['memory_records']) ? $snapshot['memory_records'] : array();
            foreach ($rows as $row) {
                if (!isset($row['memory_key'])) { continue; }
                $key = (string)$row['memory_key'];
                if (!isset($map[$key])) {
                    $map[$key] = array('memory_key'=>$key,'memory_name'=>isset($row['memory_name'])?$row['memory_name']:$key,'memory_type'=>isset($row['memory_type'])?$row['memory_type']:'PATTERN','weighted_sum'=>0.0,'weight_sum'=>0.0,'snapshot_count'=>0,'positive_count'=>0,'negative_count'=>0,'neutral_count'=>0,'first_seen'=>$snapshotDate,'last_seen'=>$snapshotDate,'values'=>array());
                }
                $ageDays = max(0, (int)floor((strtotime($date)-strtotime($snapshotDate))/86400));
                $recencyWeight = max(0.20, exp(-$ageDays/90.0));
                $sampleWeight = max(1.0, log(1.0 + max(1, (int)(isset($row['sample_size'])?$row['sample_size']:1))));
                $weight = $recencyWeight * $sampleWeight;
                $value = (float)(isset($row['memory_strength'])?$row['memory_strength']:0);
                $map[$key]['weighted_sum'] += $value*$weight;
                $map[$key]['weight_sum'] += $weight;
                $map[$key]['snapshot_count']++;
                $map[$key]['values'][] = $value;
                $sentiment = isset($row['sentiment'])?(string)$row['sentiment']:'NEUTRAL';
                if ($sentiment==='POSITIVE') $map[$key]['positive_count']++; elseif ($sentiment==='NEGATIVE') $map[$key]['negative_count']++; else $map[$key]['neutral_count']++;
                if ($snapshotDate < $map[$key]['first_seen']) $map[$key]['first_seen']=$snapshotDate;
                if ($snapshotDate > $map[$key]['last_seen']) $map[$key]['last_seen']=$snapshotDate;
            }
        }
        foreach ($currentRecords as $row) {
            $key = (string)$row['memory_key'];
            if (!isset($map[$key])) {
                $map[$key] = array('memory_key'=>$key,'memory_name'=>$row['memory_name'],'memory_type'=>$row['memory_type'],'weighted_sum'=>0.0,'weight_sum'=>0.0,'snapshot_count'=>0,'positive_count'=>0,'negative_count'=>0,'neutral_count'=>0,'first_seen'=>$date,'last_seen'=>$date,'values'=>array());
            }
            $weight=max(1.0,log(1.0+max(1,(int)$row['sample_size'])))*1.25;
            $value=(float)$row['memory_strength'];
            $map[$key]['weighted_sum'] += $value*$weight;
            $map[$key]['weight_sum'] += $weight;
            $map[$key]['snapshot_count']++;
            $map[$key]['values'][]=$value;
            if ($row['sentiment']==='POSITIVE')$map[$key]['positive_count']++;elseif($row['sentiment']==='NEGATIVE')$map[$key]['negative_count']++;else$map[$key]['neutral_count']++;
            $map[$key]['last_seen']=$date;
        }
        $profiles=array();
        foreach($map as $m){
            $fused=$m['weight_sum']>0?$m['weighted_sum']/$m['weight_sum']:0.0;
            $count=count($m['values']);$mean=$count?array_sum($m['values'])/$count:0.0;$variance=0.0;foreach($m['values'] as $v){$variance+=($v-$mean)*($v-$mean);} $std=$count?sqrt($variance/$count):0.0;
            $consistency=$this->clamp(100.0-$std*5.0);
            $coverage=$this->clamp(log(1+$m['snapshot_count'])/log(11)*100.0);
            $fusionConfidence=$this->clamp($consistency*0.45+$coverage*0.35+min(100.0,$m['weight_sum']*8.0)*0.20);
            $voteTotal=$m['positive_count']+$m['negative_count']+$m['neutral_count'];
            $voteBalance=$voteTotal?($m['positive_count']-$m['negative_count'])/$voteTotal*100.0:0.0;
            if($voteBalance>=20)$sentiment='POSITIVE';elseif($voteBalance<=-20)$sentiment='NEGATIVE';else$sentiment='NEUTRAL';
            if($fusionConfidence>=65 && $fused>=60 && $sentiment==='POSITIVE')$rec='FUSE_PROMOTE';elseif($sentiment==='NEGATIVE'&&$fusionConfidence>=55)$rec='FUSE_GUARD';elseif($consistency<55)$rec='FUSE_CAUTION';else$rec='FUSE_HOLD';
            $profiles[]=array('memory_key'=>$m['memory_key'],'memory_name'=>$m['memory_name'],'memory_type'=>$m['memory_type'],'fused_memory_score'=>round($fused,2),'fusion_confidence'=>round($fusionConfidence,2),'cross_race_consistency'=>round($consistency,2),'snapshot_coverage'=>round($coverage,2),'snapshot_count'=>$m['snapshot_count'],'vote_balance'=>round($voteBalance,2),'dominant_sentiment'=>$sentiment,'fusion_recommendation'=>$rec,'first_seen'=>$m['first_seen'],'last_seen'=>$m['last_seen']);
        }
        usort($profiles,function($a,$b){return $a['fusion_confidence']<$b['fusion_confidence']?1:($a['fusion_confidence']>$b['fusion_confidence']?-1:strcmp($a['memory_key'],$b['memory_key']));});
        $avg=$profiles?array_sum(array_column($profiles,'fusion_confidence'))/count($profiles):0.0;$promote=$guard=$caution=0;foreach($profiles as $p){if($p['fusion_recommendation']==='FUSE_PROMOTE')$promote++;elseif($p['fusion_recommendation']==='FUSE_GUARD')$guard++;elseif($p['fusion_recommendation']==='FUSE_CAUTION')$caution++;}
        return array('summary'=>array('fusion_profile_count'=>count($profiles),'history_snapshot_count'=>count($v53History),'average_fusion_confidence'=>round($avg,2),'promote_count'=>$promote,'guard_count'=>$guard,'caution_count'=>$caution,'top_fused_memory'=>isset($profiles[0])?$profiles[0]['memory_key']:'NONE'),'profiles'=>$profiles);
    }

    private function buildStrategyOptimizer(array $experiences, array $contexts, array $evolution, array $fusionProfiles)
    {
        $contextMap=array();foreach($contexts as $x){$contextMap[(string)$x['cluster_id']]=$x;}
        $evoMap=array();foreach($evolution as $x){$evoMap[(string)$x['cluster_id']]=$x;}
        $fusionByName=array();foreach($fusionProfiles as $x){$fusionByName[(string)$x['memory_key']]=$x;}
        $strategies=array();
        foreach($experiences as $e){
            $ctx=isset($contextMap[$e['cluster_id']])?$contextMap[$e['cluster_id']]:array();
            $evo=isset($evoMap[$e['cluster_id']])?$evoMap[$e['cluster_id']]:array();
            $base=(float)$e['experience_score'];$conf=(float)$e['experience_confidence'];$fit=(float)(isset($ctx['context_fit_score'])?$ctx['context_fit_score']:50);$risk=(float)$e['graph_risk_score'];
            $evolutionBonus=isset($evo['evolution_state'])&&in_array($evo['evolution_state'],array('GROWING','STABLE'),true)?8.0:(isset($evo['evolution_state'])&&in_array($evo['evolution_state'],array('DECLINING','RETIRE_CANDIDATE'),true)?-10.0:0.0);
            $action=isset($ctx['context_action'])?$ctx['context_action']:'MONITOR';
            $score=$this->clamp($base*0.34+$conf*0.22+$fit*0.24+(100.0-$risk)*0.12+50.0*0.08+$evolutionBonus);
            if($e['verdict']==='TRUST'&&$action==='SUPPORT'&&$score>=68)$strategy='AGGRESSIVE_SUPPORT';elseif($e['verdict']==='AVOID'||$action==='BLOCK')$strategy='DEFENSIVE_GUARD';elseif($action==='REDUCE'||$e['verdict']==='CAUTION')$strategy='REDUCED_EXPOSURE';elseif($score>=55)$strategy='SELECTIVE_SUPPORT';else$strategy='OBSERVE_ONLY';
            if($strategy==='AGGRESSIVE_SUPPORT')$allocation=1.15;elseif($strategy==='SELECTIVE_SUPPORT')$allocation=0.85;elseif($strategy==='REDUCED_EXPOSURE')$allocation=0.45;elseif($strategy==='DEFENSIVE_GUARD')$allocation=0.0;else$allocation=0.20;
            $confidence=$this->clamp($conf*0.42+$fit*0.30+(100.0-$risk)*0.18+min(100.0,$e['sample_size']*5.0)*0.10);
            $strategies[]=array('strategy_id'=>'STR-'.$e['cluster_id'],'cluster_id'=>$e['cluster_id'],'strategy_name'=>$strategy,'strategy_score'=>round($score,2),'strategy_confidence'=>round($confidence,2),'allocation_multiplier'=>$allocation,'experience_verdict'=>$e['verdict'],'context_action'=>$action,'evolution_state'=>isset($evo['evolution_state'])?$evo['evolution_state']:'UNKNOWN','risk_score'=>round($risk,2),'recommendation'=>$strategy==='DEFENSIVE_GUARD'?'BLOCK':($strategy==='AGGRESSIVE_SUPPORT'?'PROMOTE':($strategy==='SELECTIVE_SUPPORT'?'SUPPORT':($strategy==='REDUCED_EXPOSURE'?'REDUCE':'HOLD'))),'reason'=>'Experience '.round($base,1).' / confidence '.round($conf,1).' / context fit '.round($fit,1).' / risk '.round($risk,1).' / evolution '.(isset($evo['evolution_state'])?$evo['evolution_state']:'UNKNOWN').'.');
        }
        usort($strategies,function($a,$b){return $a['strategy_score']<$b['strategy_score']?1:($a['strategy_score']>$b['strategy_score']?-1:strcmp($a['strategy_id'],$b['strategy_id']));});
        $counts=array('AGGRESSIVE_SUPPORT'=>0,'SELECTIVE_SUPPORT'=>0,'REDUCED_EXPOSURE'=>0,'DEFENSIVE_GUARD'=>0,'OBSERVE_ONLY'=>0);foreach($strategies as $x){$counts[$x['strategy_name']]++;}
        return array('summary'=>array('strategy_count'=>count($strategies),'aggressive_count'=>$counts['AGGRESSIVE_SUPPORT'],'selective_count'=>$counts['SELECTIVE_SUPPORT'],'reduced_count'=>$counts['REDUCED_EXPOSURE'],'guard_count'=>$counts['DEFENSIVE_GUARD'],'observe_count'=>$counts['OBSERVE_ONLY'],'top_strategy'=>isset($strategies[0])?$strategies[0]['strategy_id']:'NONE'),'strategies'=>$strategies);
    }

    private function buildExplainableChairman(array $strategies, array $experiences, array $contexts, array $fusionProfiles)
    {
        $experienceMap=array();foreach($experiences as $x){$experienceMap[(string)$x['cluster_id']]=$x;}
        $contextMap=array();foreach($contexts as $x){$contextMap[(string)$x['cluster_id']]=$x;}
        $explanations=array();
        foreach($strategies as $s){
            $e=isset($experienceMap[$s['cluster_id']])?$experienceMap[$s['cluster_id']]:array();$c=isset($contextMap[$s['cluster_id']])?$contextMap[$s['cluster_id']]:array();
            $drivers=array();
            $drivers[]=array('factor'=>'EXPERIENCE','impact'=>round(((float)(isset($e['experience_score'])?$e['experience_score']:0)-50.0)*0.34,2),'evidence'=>'Experience verdict '.(isset($e['verdict'])?$e['verdict']:'UNKNOWN'));
            $drivers[]=array('factor'=>'CONTEXT','impact'=>round(((float)(isset($c['context_fit_score'])?$c['context_fit_score']:50)-50.0)*0.24,2),'evidence'=>'Context action '.(isset($c['context_action'])?$c['context_action']:'MONITOR'));
            $drivers[]=array('factor'=>'RISK','impact'=>round((50.0-(float)$s['risk_score'])*0.12,2),'evidence'=>'Graph risk '.round((float)$s['risk_score'],1));
            $drivers[]=array('factor'=>'EVOLUTION','impact'=>in_array($s['evolution_state'],array('GROWING','STABLE'),true)?8.0:(in_array($s['evolution_state'],array('DECLINING','RETIRE_CANDIDATE'),true)?-10.0:0.0),'evidence'=>'Evolution '.$s['evolution_state']);
            usort($drivers,function($a,$b){return abs($a['impact'])<abs($b['impact'])?1:-1;});
            $positive=array_values(array_filter($drivers,function($d){return $d['impact']>0;}));$negative=array_values(array_filter($drivers,function($d){return $d['impact']<0;}));
            $summary='Chairman selects '.$s['strategy_name'].' because '.(isset($drivers[0])?$drivers[0]['evidence']:'combined evidence').' is the strongest driver.';
            if($negative){$summary.=' Main caution: '.$negative[0]['evidence'].'.';}
            $explanations[]=array('explanation_id'=>'XAI-'.$s['cluster_id'],'cluster_id'=>$s['cluster_id'],'final_strategy'=>$s['strategy_name'],'final_score'=>$s['strategy_score'],'confidence'=>$s['strategy_confidence'],'decision_summary'=>$summary,'top_positive_reason'=>isset($positive[0])?$positive[0]['evidence']:'NONE','top_caution_reason'=>isset($negative[0])?$negative[0]['evidence']:'NONE','drivers'=>$drivers,'safety_note'=>'Advisory only. V44-V52 production weights and decisions remain unchanged.');
        }
        return array('summary'=>array('explanation_count'=>count($explanations),'high_confidence_count'=>count(array_filter($explanations,function($x){return $x['confidence']>=70;})),'top_explanation'=>isset($explanations[0])?$explanations[0]['explanation_id']:'NONE'),'explanations'=>$explanations);
    }


    private function buildDecisionSimulator(array $strategies)
    {
        $items = array();
        foreach ($strategies as $s) {
            $base = (float)$s['strategy_score'];
            $risk = (float)$s['risk_score'];
            $confidence = (float)$s['strategy_confidence'];
            $allocation = (float)$s['allocation_multiplier'];
            $promoteDelta = $this->clamp(($confidence - 50.0) * 0.065 + (50.0 - $risk) * 0.035 + $allocation * 1.4, -6.0, 8.0);
            $guardDelta = $this->clamp(($risk - 45.0) * 0.055 - max(0.0, $confidence - 65.0) * 0.025, -5.0, 6.0);
            $ignoreDelta = $this->clamp((50.0 - $base) * 0.035 - abs($allocation - 0.5) * 0.8, -4.0, 4.0);
            $scenarios = array(
                array('scenario'=>'PROMOTE','score_delta'=>round($promoteDelta,2),'simulated_score'=>round($this->clamp($base+$promoteDelta),2),'risk_delta'=>round(max(-3.0,min(5.0,2.2-$risk*0.018)),2)),
                array('scenario'=>'GUARD','score_delta'=>round($guardDelta,2),'simulated_score'=>round($this->clamp($base+$guardDelta),2),'risk_delta'=>round(-min(6.0,max(1.0,$risk*0.08)),2)),
                array('scenario'=>'IGNORE','score_delta'=>round($ignoreDelta,2),'simulated_score'=>round($this->clamp($base+$ignoreDelta),2),'risk_delta'=>0.0)
            );
            foreach ($scenarios as &$sc) {
                $sc['expected_gain'] = round($sc['score_delta'] - max(0.0,$sc['risk_delta'])*0.45,2);
            }
            unset($sc);
            usort($scenarios,function($a,$b){return $a['expected_gain']<$b['expected_gain']?1:($a['expected_gain']>$b['expected_gain']?-1:0);});
            $best=$scenarios[0];
            $items[]=array('simulation_id'=>'SIM-'.$s['cluster_id'],'cluster_id'=>$s['cluster_id'],'source_strategy'=>$s['strategy_name'],'baseline_score'=>round($base,2),'baseline_risk'=>round($risk,2),'scenarios'=>$scenarios,'best_scenario'=>$best['scenario'],'best_expected_gain'=>$best['expected_gain'],'simulation_confidence'=>round($this->clamp($confidence*0.65+(100.0-$risk)*0.35),2),'reason'=>'Three-way advisory simulation over immutable V53.8 strategy output.');
        }
        usort($items,function($a,$b){return $a['best_expected_gain']<$b['best_expected_gain']?1:($a['best_expected_gain']>$b['best_expected_gain']?-1:strcmp($a['simulation_id'],$b['simulation_id']));});
        $promote=$guard=$ignore=0;foreach($items as $x){if($x['best_scenario']==='PROMOTE')$promote++;elseif($x['best_scenario']==='GUARD')$guard++;else$ignore++;}
        return array('summary'=>array('simulation_count'=>count($items),'promote_best_count'=>$promote,'guard_best_count'=>$guard,'ignore_best_count'=>$ignore,'top_simulation'=>isset($items[0])?$items[0]['simulation_id']:'NONE'),'simulations'=>$items);
    }

    private function buildCounterfactualAI(array $simulations)
    {
        $items=array();
        foreach($simulations as $sim){
            $scores=array();$gains=array();foreach($sim['scenarios'] as $sc){$scores[$sc['scenario']]=(float)$sc['simulated_score'];$gains[$sc['scenario']]=(float)$sc['expected_gain'];}
            $spread=$scores?max($scores)-min($scores):0.0;
            $essentiality=$this->clamp($spread*12.0+(float)$sim['simulation_confidence']*0.35);
            if($essentiality>=72)$class='ESSENTIAL';elseif($essentiality>=52)$class='MATERIAL';elseif($essentiality>=32)$class='SUPPORTING';else$class='FAKE_IMPORTANT';
            $withoutPromote=isset($scores['IGNORE'])?$scores['IGNORE']:$sim['baseline_score'];
            $withPromote=isset($scores['PROMOTE'])?$scores['PROMOTE']:$sim['baseline_score'];
            $impact=$withPromote-$withoutPromote;
            $items[]=array('counterfactual_id'=>'CF-'.$sim['cluster_id'],'cluster_id'=>$sim['cluster_id'],'importance_class'=>$class,'essentiality_score'=>round($essentiality,2),'outcome_spread'=>round($spread,2),'promote_vs_ignore_impact'=>round($impact,2),'best_scenario'=>$sim['best_scenario'],'counterfactual_question'=>'What changes if this pattern is ignored instead of promoted?','counterfactual_answer'=>abs($impact)<1.0?'Little changes; importance may be overstated.':($impact>0?'Promotion materially improves the simulated decision.':'Promotion weakens the simulated decision; guard or ignore is safer.'),'fake_importance_flag'=>$class==='FAKE_IMPORTANT');
        }
        usort($items,function($a,$b){return $a['essentiality_score']<$b['essentiality_score']?1:($a['essentiality_score']>$b['essentiality_score']?-1:strcmp($a['counterfactual_id'],$b['counterfactual_id']));});
        $essential=$fake=0;foreach($items as $x){if($x['importance_class']==='ESSENTIAL')$essential++;if($x['fake_importance_flag'])$fake++;}
        return array('summary'=>array('counterfactual_count'=>count($items),'essential_count'=>$essential,'fake_important_count'=>$fake,'top_essential_pattern'=>isset($items[0])?$items[0]['cluster_id']:'NONE'),'counterfactuals'=>$items);
    }

    private function buildSelfDebate(array $strategies, array $counterfactuals)
    {
        $cfMap=array();foreach($counterfactuals as $x){$cfMap[$x['cluster_id']]=$x;}
        $debates=array();
        foreach($strategies as $s){
            $cf=isset($cfMap[$s['cluster_id']])?$cfMap[$s['cluster_id']]:array();
            $score=(float)$s['strategy_score'];$risk=(float)$s['risk_score'];$conf=(float)$s['strategy_confidence'];$essential=(float)(isset($cf['essentiality_score'])?$cf['essentiality_score']:50);
            $advocate=$this->clamp($score*0.55+$conf*0.30+$essential*0.15);
            $skeptic=$this->clamp($risk*0.55+(100.0-$conf)*0.30+(100.0-$essential)*0.15);
            $riskAgent=$this->clamp($risk*0.70+(100.0-$score)*0.30);
            $net=$advocate-$skeptic-$riskAgent*0.18;
            if($net>=18)$verdict='PROMOTE';elseif($net>=5)$verdict='SUPPORT';elseif($net<=-18)$verdict='GUARD';elseif($net<=-5)$verdict='REDUCE';else$verdict='HOLD';
            $chairmanConfidence=$this->clamp(50.0+abs($net)*1.35+min(15.0,$conf*0.12));
            $debates[]=array('debate_id'=>'DEB-'.$s['cluster_id'],'cluster_id'=>$s['cluster_id'],'agent_a'=>array('role'=>'ADVOCATE','position'=>'PROMOTE','score'=>round($advocate,2),'argument'=>'Strategy strength, confidence and counterfactual importance support action.'),'agent_b'=>array('role'=>'SKEPTIC','position'=>'HOLD_OR_REDUCE','score'=>round($skeptic,2),'argument'=>'Uncertainty and possible fake importance argue for restraint.'),'agent_c'=>array('role'=>'RISK_GUARD','position'=>'GUARD','score'=>round($riskAgent,2),'argument'=>'Graph and strategy risk require exposure control.'),'chairman_verdict'=>$verdict,'chairman_confidence'=>round($chairmanConfidence,2),'debate_margin'=>round($net,2),'final_allocation_multiplier'=>$verdict==='PROMOTE'?1.10:($verdict==='SUPPORT'?0.85:($verdict==='REDUCE'?0.45:($verdict==='GUARD'?0.0:0.20))),'chairman_reason'=>'Advocate '.round($advocate,1).' vs Skeptic '.round($skeptic,1).' and Risk Guard '.round($riskAgent,1).'; counterfactual class '.(isset($cf['importance_class'])?$cf['importance_class']:'UNKNOWN').'.');
        }
        usort($debates,function($a,$b){return $a['chairman_confidence']<$b['chairman_confidence']?1:($a['chairman_confidence']>$b['chairman_confidence']?-1:strcmp($a['debate_id'],$b['debate_id']));});
        $counts=array('PROMOTE'=>0,'SUPPORT'=>0,'HOLD'=>0,'REDUCE'=>0,'GUARD'=>0);foreach($debates as $x){$counts[$x['chairman_verdict']]++;}
        return array('summary'=>array('debate_count'=>count($debates),'promote_count'=>$counts['PROMOTE'],'support_count'=>$counts['SUPPORT'],'hold_count'=>$counts['HOLD'],'reduce_count'=>$counts['REDUCE'],'guard_count'=>$counts['GUARD'],'top_debate'=>isset($debates[0])?$debates[0]['debate_id']:'NONE'),'debates'=>$debates);
    }


    private function buildDebateConsensusStabilizer(array $debates)
    {
        $items=array();
        foreach($debates as $d){
            $adv=(float)$d['agent_a']['score']; $ske=(float)$d['agent_b']['score']; $risk=(float)$d['agent_c']['score'];
            $margin=(float)$d['debate_margin']; $chair=(float)$d['chairman_confidence'];
            $dispersion=max($adv,$ske,$risk)-min($adv,$ske,$risk);
            $agreement=$this->clamp(100.0-$dispersion*1.15);
            $stability=$this->clamp($agreement*0.45+$chair*0.40+min(100.0,abs($margin)*4.0)*0.15);
            if($stability>=78)$state='STABLE'; elseif($stability>=58)$state='CONTROLLED'; elseif($stability>=38)$state='VOLATILE'; else $state='UNSTABLE';
            $verdict=$d['chairman_verdict'];
            if($state==='UNSTABLE' && in_array($verdict,array('PROMOTE','SUPPORT'),true))$stabilized='HOLD';
            elseif($state==='VOLATILE' && $verdict==='PROMOTE')$stabilized='SUPPORT';
            else $stabilized=$verdict;
            $items[]=array(
                'stabilizer_id'=>'STAB-'.$d['cluster_id'],'cluster_id'=>$d['cluster_id'],
                'original_verdict'=>$verdict,'stabilized_verdict'=>$stabilized,
                'agent_agreement'=>round($agreement,2),'debate_dispersion'=>round($dispersion,2),
                'stability_score'=>round($stability,2),'stability_state'=>$state,
                'confidence_after_stabilization'=>round($this->clamp($chair*0.72+$stability*0.28),2),
                'stabilization_reason'=>$stabilized===$verdict?'Original verdict retained after stability review.':'Verdict moderated because agent disagreement is too high.'
            );
        }
        usort($items,function($a,$b){return $a['stability_score']<$b['stability_score']?1:($a['stability_score']>$b['stability_score']?-1:strcmp($a['stabilizer_id'],$b['stabilizer_id']));});
        $stable=$controlled=$volatile=$unstable=$changed=0;
        foreach($items as $x){if($x['stability_state']==='STABLE')$stable++;elseif($x['stability_state']==='CONTROLLED')$controlled++;elseif($x['stability_state']==='VOLATILE')$volatile++;else$unstable++;if($x['original_verdict']!==$x['stabilized_verdict'])$changed++;}
        return array('summary'=>array('stabilized_count'=>count($items),'stable_count'=>$stable,'controlled_count'=>$controlled,'volatile_count'=>$volatile,'unstable_count'=>$unstable,'verdicts_changed'=>$changed,'average_stability'=>count($items)?round(array_sum(array_column($items,'stability_score'))/count($items),2):0),'items'=>$items);
    }

    private function buildScenarioStressTest(array $simulations, array $stabilized)
    {
        $stab=array(); foreach($stabilized as $x)$stab[$x['cluster_id']]=$x;
        $items=array();
        foreach($simulations as $sim){
            $base=(float)$sim['baseline_score']; $risk=(float)$sim['baseline_risk'];
            $st=isset($stab[$sim['cluster_id']])?$stab[$sim['cluster_id']]:array('stability_score'=>50,'stabilized_verdict'=>'HOLD');
            $shocks=array(
                array('name'=>'NORMAL','score_shock'=>0.0,'risk_shock'=>0.0),
                array('name'=>'MARKET_REVERSAL','score_shock'=>-6.0,'risk_shock'=>8.0),
                array('name'=>'HIGH_CONFLICT','score_shock'=>-4.0,'risk_shock'=>12.0),
                array('name'=>'DATA_WEAKNESS','score_shock'=>-8.0,'risk_shock'=>5.0)
            );
            $rows=array();$worst=999;$pass=0;
            foreach($shocks as $z){
                $res=$this->clamp($base+$z['score_shock']+(((float)$st['stability_score']-50.0)*0.05));
                $rr=$this->clamp($risk+$z['risk_shock']);
                $ok=($res>=45.0 && $rr<=72.0); if($ok)$pass++;
                if($res<$worst)$worst=$res;
                $rows[]=array('scenario'=>$z['name'],'stressed_score'=>round($res,2),'stressed_risk'=>round($rr,2),'pass'=>$ok);
            }
            $resilience=$this->clamp(($pass/count($shocks))*65.0+max(0.0,$worst-30.0)*1.2);
            if($resilience>=75)$grade='A';elseif($resilience>=60)$grade='B';elseif($resilience>=45)$grade='C';else$grade='D';
            $items[]=array('stress_id'=>'STRESS-'.$sim['cluster_id'],'cluster_id'=>$sim['cluster_id'],'source_verdict'=>$st['stabilized_verdict'],'stress_grade'=>$grade,'resilience_score'=>round($resilience,2),'pass_count'=>$pass,'scenario_count'=>count($shocks),'worst_case_score'=>round($worst,2),'scenarios'=>$rows,'stress_recommendation'=>$grade==='A'?'PROCEED':($grade==='B'?'SELECTIVE':($grade==='C'?'REDUCE':'BLOCK')));
        }
        usort($items,function($a,$b){return $a['resilience_score']<$b['resilience_score']?1:($a['resilience_score']>$b['resilience_score']?-1:strcmp($a['stress_id'],$b['stress_id']));});
        $grades=array('A'=>0,'B'=>0,'C'=>0,'D'=>0);foreach($items as $x)$grades[$x['stress_grade']]++;
        return array('summary'=>array('stress_test_count'=>count($items),'grade_a'=>$grades['A'],'grade_b'=>$grades['B'],'grade_c'=>$grades['C'],'grade_d'=>$grades['D'],'top_resilient'=>isset($items[0])?$items[0]['cluster_id']:'NONE'),'items'=>$items);
    }

    private function buildChairmanDecisionGate(array $stabilized, array $stressTests)
    {
        $stress=array();foreach($stressTests as $x)$stress[$x['cluster_id']]=$x;
        $items=array();
        foreach($stabilized as $st){
            $ss=isset($stress[$st['cluster_id']])?$stress[$st['cluster_id']]:array('resilience_score'=>0,'stress_grade'=>'D','stress_recommendation'=>'BLOCK');
            $score=$this->clamp((float)$st['confidence_after_stabilization']*0.42+(float)$st['stability_score']*0.28+(float)$ss['resilience_score']*0.30);
            $v=$st['stabilized_verdict'];
            if($ss['stress_recommendation']==='BLOCK' || $score<40)$gate='BLOCKED';
            elseif($ss['stress_recommendation']==='REDUCE' || $score<55)$gate='LIMITED';
            elseif(in_array($v,array('PROMOTE','SUPPORT'),true) && $score>=72)$gate='APPROVED';
            else $gate='REVIEW';
            $mult=$gate==='APPROVED'?1.0:($gate==='REVIEW'?0.65:($gate==='LIMITED'?0.30:0.0));
            $items[]=array('gate_id'=>'GATE-'.$st['cluster_id'],'cluster_id'=>$st['cluster_id'],'input_verdict'=>$v,'gate_status'=>$gate,'gate_score'=>round($score,2),'approved_multiplier'=>$mult,'stress_grade'=>$ss['stress_grade'],'stability_state'=>$st['stability_state'],'final_advisory_action'=>$gate==='APPROVED'?$v:($gate==='REVIEW'?'HOLD':($gate==='LIMITED'?'REDUCE':'GUARD')),'gate_reason'=>'Stability '.$st['stability_state'].'; stress grade '.$ss['stress_grade'].'; combined gate score '.round($score,1).'.');
        }
        usort($items,function($a,$b){return $a['gate_score']<$b['gate_score']?1:($a['gate_score']>$b['gate_score']?-1:strcmp($a['gate_id'],$b['gate_id']));});
        $counts=array('APPROVED'=>0,'REVIEW'=>0,'LIMITED'=>0,'BLOCKED'=>0);foreach($items as $x)$counts[$x['gate_status']]++;
        $status=$counts['BLOCKED']>0?'CAUTION':($counts['APPROVED']>0?'READY':'REVIEW');
        return array('summary'=>array('gate_count'=>count($items),'approved_count'=>$counts['APPROVED'],'review_count'=>$counts['REVIEW'],'limited_count'=>$counts['LIMITED'],'blocked_count'=>$counts['BLOCKED'],'overall_gate_status'=>$status,'top_approved'=>isset($items[0])?$items[0]['cluster_id']:'NONE'),'items'=>$items);
    }


    private function buildDecisionAudit(array $gates, array $stabilized, array $stressTests)
    {
        $stab=array(); foreach($stabilized as $x)$stab[$x['cluster_id']]=$x;
        $stress=array(); foreach($stressTests as $x)$stress[$x['cluster_id']]=$x;
        $items=array(); $passed=0; $warning=0; $failed=0;
        foreach($gates as $g){
            $cid=$g['cluster_id']; $st=isset($stab[$cid])?$stab[$cid]:array(); $ss=isset($stress[$cid])?$stress[$cid]:array();
            $checks=array(
                'gate_score_valid'=>isset($g['gate_score']) && is_numeric($g['gate_score']) && (float)$g['gate_score']>=0 && (float)$g['gate_score']<=100,
                'multiplier_safe'=>isset($g['approved_multiplier']) && (float)$g['approved_multiplier']>=0 && (float)$g['approved_multiplier']<=1,
                'stress_present'=>isset($ss['stress_grade']),
                'stability_present'=>isset($st['stability_state']),
                'advisory_action_present'=>isset($g['final_advisory_action']) && (string)$g['final_advisory_action']!==''
            );
            $ok=count(array_filter($checks)); $total=count($checks); $ratio=$total?($ok/$total):0;
            if($ratio>=1){$status='PASS';$passed++;}elseif($ratio>=0.6){$status='WARNING';$warning++;}else{$status='FAIL';$failed++;}
            $items[]=array('audit_id'=>'AUDIT-'.$cid,'cluster_id'=>$cid,'audit_status'=>$status,'audit_score'=>round($ratio*100,2),'checks'=>$checks,'gate_status'=>$g['gate_status'],'final_advisory_action'=>$g['final_advisory_action'],'audit_reason'=>$ok.' of '.$total.' release checks passed.');
        }
        usort($items,function($a,$b){return $a['audit_score']<$b['audit_score']?1:($a['audit_score']>$b['audit_score']?-1:strcmp($a['audit_id'],$b['audit_id']));});
        return array('summary'=>array('audit_count'=>count($items),'pass_count'=>$passed,'warning_count'=>$warning,'fail_count'=>$failed,'audit_ready'=>$failed===0,'average_audit_score'=>count($items)?round(array_sum(array_column($items,'audit_score'))/count($items),2):0),'items'=>$items);
    }

    private function buildSafetyGovernor(array $gates, array $audits, array $stressTests)
    {
        $audit=array(); foreach($audits as $x)$audit[$x['cluster_id']]=$x;
        $stress=array(); foreach($stressTests as $x)$stress[$x['cluster_id']]=$x;
        $items=array(); $open=0;$capped=0;$locked=0;
        foreach($gates as $g){
            $cid=$g['cluster_id']; $a=isset($audit[$cid])?$audit[$cid]:array('audit_status'=>'FAIL','audit_score'=>0); $ss=isset($stress[$cid])?$stress[$cid]:array('stress_grade'=>'D','resilience_score'=>0);
            $base=(float)$g['approved_multiplier']; $score=$this->clamp((float)$g['gate_score']*0.45+(float)$a['audit_score']*0.30+(float)$ss['resilience_score']*0.25);
            if($a['audit_status']==='FAIL' || $g['gate_status']==='BLOCKED' || $ss['stress_grade']==='D'){$state='LOCKED';$mult=0.0;$action='GUARD';$locked++;}
            elseif($a['audit_status']==='WARNING' || $g['gate_status']==='LIMITED' || $ss['stress_grade']==='C'){$state='CAPPED';$mult=min($base,0.30);$action='REDUCE';$capped++;}
            else{$state='OPEN';$mult=min($base,1.0);$action=$g['final_advisory_action'];$open++;}
            $items[]=array('governor_id'=>'SAFE-'.$cid,'cluster_id'=>$cid,'safety_state'=>$state,'safety_score'=>round($score,2),'input_multiplier'=>$base,'governed_multiplier'=>round($mult,2),'governed_action'=>$action,'audit_status'=>$a['audit_status'],'stress_grade'=>$ss['stress_grade'],'safety_reason'=>'Gate '.$g['gate_status'].'; audit '.$a['audit_status'].'; stress '.$ss['stress_grade'].'.');
        }
        usort($items,function($a,$b){return $a['safety_score']<$b['safety_score']?1:($a['safety_score']>$b['safety_score']?-1:strcmp($a['governor_id'],$b['governor_id']));});
        return array('summary'=>array('governed_count'=>count($items),'open_count'=>$open,'capped_count'=>$capped,'locked_count'=>$locked,'overall_safety_state'=>$locked>0?'GUARDED':($capped>0?'CONTROLLED':'OPEN'),'average_safety_score'=>count($items)?round(array_sum(array_column($items,'safety_score'))/count($items),2):0),'items'=>$items);
    }

    private function buildFinalChairmanRanking(array $governed, array $gates, array $strategies)
    {
        $gate=array(); foreach($gates as $x)$gate[$x['cluster_id']]=$x;
        $strategy=array(); foreach($strategies as $x)$strategy[$x['cluster_id']]=$x;
        $items=array();
        foreach($governed as $x){
            $cid=$x['cluster_id']; $g=isset($gate[$cid])?$gate[$cid]:array('gate_score'=>0); $st=isset($strategy[$cid])?$strategy[$cid]:array('strategy_score'=>0,'strategy_confidence'=>0);
            $score=$this->clamp((float)$x['safety_score']*0.40+(float)$g['gate_score']*0.30+(float)$st['strategy_score']*0.20+(float)$st['strategy_confidence']*0.10);
            if($x['safety_state']==='LOCKED')$score=min($score,39.9); elseif($x['safety_state']==='CAPPED')$score=min($score,64.9);
            $items[]=array('ranking_id'=>'FINAL-'.$cid,'cluster_id'=>$cid,'final_score'=>round($score,2),'final_action'=>$x['governed_action'],'final_multiplier'=>$x['governed_multiplier'],'safety_state'=>$x['safety_state'],'gate_status'=>isset($g['gate_status'])?$g['gate_status']:'REVIEW','strategy'=>isset($st['strategy'])?$st['strategy']:'OBSERVE_ONLY','ranking_reason'=>'Safety-governed final ranking; immutable source scores preserved.');
        }
        usort($items,function($a,$b){return $a['final_score']<$b['final_score']?1:($a['final_score']>$b['final_score']?-1:strcmp($a['cluster_id'],$b['cluster_id']));});
        $rank=1;$approved=0;$review=0;$guard=0; foreach($items as &$x){$x['final_rank']=$rank++; if(in_array($x['final_action'],array('PROMOTE','SUPPORT'),true))$approved++; elseif(in_array($x['final_action'],array('HOLD','WATCH'),true))$review++; else$guard++;} unset($x);
        return array('summary'=>array('ranked_count'=>count($items),'support_count'=>$approved,'review_count'=>$review,'guard_count'=>$guard,'top_ranked'=>isset($items[0])?$items[0]['cluster_id']:'NONE','top_score'=>isset($items[0])?$items[0]['final_score']:0),'items'=>$items);
    }


    private function buildUltimateDashboard(array $ranking, array $safety, array $audits)
    {
        $safe=array(); foreach($safety as $x)$safe[$x['cluster_id']]=$x;
        $audit=array(); foreach($audits as $x)$audit[$x['cluster_id']]=$x;
        $cards=array(); $actionable=0; $watch=0; $guarded=0;
        foreach($ranking as $x){
            $cid=$x['cluster_id']; $sf=isset($safe[$cid])?$safe[$cid]:array('safety_score'=>0,'safety_state'=>'LOCKED'); $au=isset($audit[$cid])?$audit[$cid]:array('audit_score'=>0,'audit_status'=>'FAIL');
            $decision=(string)$x['final_action'];
            if(in_array($decision,array('PROMOTE','SUPPORT'),true)){$bucket='ACTIONABLE';$actionable++;}
            elseif(in_array($decision,array('HOLD','WATCH'),true)){$bucket='WATCHLIST';$watch++;}
            else{$bucket='GUARDED';$guarded++;}
            $cards[]=array(
                'dashboard_id'=>'DASH-'.$cid,
                'cluster_id'=>$cid,
                'final_rank'=>$x['final_rank'],
                'final_score'=>$x['final_score'],
                'decision'=>$decision,
                'decision_bucket'=>$bucket,
                'multiplier'=>$x['final_multiplier'],
                'safety_score'=>isset($sf['safety_score'])?$sf['safety_score']:0,
                'safety_state'=>isset($sf['safety_state'])?$sf['safety_state']:'LOCKED',
                'audit_score'=>isset($au['audit_score'])?$au['audit_score']:0,
                'audit_status'=>isset($au['audit_status'])?$au['audit_status']:'FAIL',
                'dashboard_reason'=>'One-screen executive view over final ranking, audit and safety governor.'
            );
        }
        return array('summary'=>array(
            'dashboard_count'=>count($cards),
            'actionable_count'=>$actionable,
            'watchlist_count'=>$watch,
            'guarded_count'=>$guarded,
            'top_cluster'=>isset($cards[0])?$cards[0]['cluster_id']:'NONE',
            'top_score'=>isset($cards[0])?$cards[0]['final_score']:0,
            'dashboard_state'=>$guarded>0?'CONTROLLED':($watch>0?'MONITORED':'CLEAR')
        ),'items'=>$cards);
    }

    private function buildFinalRelease(array $dashboardSummary, array $auditSummary, array $safetySummary, array $rankingSummary)
    {
        $checks=array(
            'audit_ready'=>!empty($auditSummary['audit_ready']),
            'no_locked_safety'=>isset($safetySummary['locked_count']) && (int)$safetySummary['locked_count']===0,
            'ranking_available'=>isset($rankingSummary['ranked_count']) && (int)$rankingSummary['ranked_count']>0,
            'dashboard_available'=>isset($dashboardSummary['dashboard_count']) && (int)$dashboardSummary['dashboard_count']>0,
            'advisory_only'=>true,
            'schema_unchanged'=>true
        );
        $passed=count(array_filter($checks)); $total=count($checks);
        $readiness=$total?round($passed/$total*100,2):0;
        $state=$readiness>=100?'ULTIMATE_READY':($readiness>=80?'READY_WITH_GUARD':'REVIEW');
        return array(
            'release_version'=>'54.9.0',
            'release_name'=>'WinRacing V54 Ultimate Final Release',
            'release_state'=>$state,
            'release_readiness'=>$readiness,
            'checks'=>$checks,
            'passed_checks'=>$passed,
            'total_checks'=>$total,
            'immutable_source_logic'=>true,
            'live_weights_changed'=>false,
            'schema_changed'=>false,
            'release_note'=>'V54.8 executive dashboard and V54.9 final release certification over immutable V44-V54.7 source logic.'
        );
    }

    public function latest($date, $venue = '')
    {
        $v52 = $this->v52->latest($date, $venue);
        if (!isset($v52['status']) || $v52['status'] !== 'OK') {
            return array('status'=>isset($v52['status'])?$v52['status']:'EMPTY','message'=>isset($v52['message'])?$v52['message']:'No V52 source data.','engine'=>'V53','engine_version'=>'54.9.0','race_date'=>$date,'venue'=>$venue,'source_v52_version'=>isset($v52['engine_version'])?$v52['engine_version']:'','memory_summary'=>array(),'memory_records'=>array(),'memory_votes'=>array(),'pattern_clusters'=>array(),'pattern_cluster_summary'=>array(),'pattern_cluster_mapping'=>array(),'relationship_graph_summary'=>array(),'relationship_graph_nodes'=>array(),'relationship_graph_edges'=>array(),'relationship_graph_node_metrics'=>array(),'experience_summary'=>array(),'chairman_experiences'=>array(),'context_awareness_summary'=>array(),'context_profiles'=>array(),'pattern_evolution_summary'=>array(),'pattern_evolution'=>array(),'multi_race_fusion_summary'=>array(),'multi_race_fusion_profiles'=>array(),'strategy_optimizer_summary'=>array(),'chairman_strategies'=>array(),'explainable_ai_summary'=>array(),'chairman_explanations'=>array(),'decision_simulator_summary'=>array(),'decision_simulations'=>array(),'counterfactual_ai_summary'=>array(),'counterfactual_analysis'=>array(),'self_debate_summary'=>array(),'chairman_debates'=>array(),'debate_stabilizer_summary'=>array(),'debate_stabilizers'=>array(),'stress_test_summary'=>array(),'scenario_stress_tests'=>array(),'decision_gate_summary'=>array(),'chairman_decision_gates'=>array(),'decision_audit_summary'=>array(),'decision_audits'=>array(),'safety_governor_summary'=>array(),'safety_governor'=>array(),'final_ranking_summary'=>array(),'final_chairman_ranking'=>array(),'ultimate_dashboard_summary'=>array(),'ultimate_dashboard'=>array(),'final_release'=>array(),'learning_mode'=>'ADVISORY_ONLY','live_weights_changed'=>false);
        }
        $history = $this->loadHistory($date, $venue);
        $hasCurrent = false;
        foreach ($history as $h) { if (isset($h['race_date']) && $h['race_date']===$date) { $hasCurrent=true; break; } }
        if (!$hasCurrent) { $history[] = $v52; }
        $map = array();
        foreach ($history as $p) { $this->absorb($map, $p); }
        $records = $this->finalize($map, $date);
        $positive=$negative=$neutral=$promote=$guard=$caution=0;
        foreach ($records as $r) {
            if ($r['sentiment']==='POSITIVE') $positive++; elseif ($r['sentiment']==='NEGATIVE') $negative++; else $neutral++;
            if ($r['recommendation']==='PROMOTE') $promote++; elseif ($r['recommendation']==='GUARD') $guard++; elseif ($r['recommendation']==='CAUTION') $caution++;
        }
        $avgStrength = $records ? array_sum(array_column($records,'memory_strength'))/count($records) : 0.0;
        $state = count($history)>=5 && count($records)>=12 ? 'ACTIVE' : (count($history)>=2 ? 'SEEDING' : 'COLD_START');
        $topPositive = array_values(array_filter($records,function($r){return $r['sentiment']==='POSITIVE';}));
        $topNegative = array_values(array_filter($records,function($r){return $r['sentiment']==='NEGATIVE';}));
        $clusterData = $this->buildClusters($records);
        $graphData = $this->buildRelationshipGraph($clusterData['clusters'], $records, $clusterData['mapping']);
        $experienceData = $this->buildExperienceEngine($records, $clusterData['clusters'], $graphData['node_metrics'], $clusterData['mapping']);
        $contextData = $this->buildContextAwareness($experienceData['experiences'], $clusterData['clusters'], $records);
        $evolutionData = $this->buildPatternEvolution($records, $clusterData['mapping'], $contextData['contexts'], $date, count($history));
        $v53History = $this->loadV53History($date, $venue);
        $fusionData = $this->buildMultiRaceFusion($records, $v53History, $date);
        $strategyData = $this->buildStrategyOptimizer($experienceData['experiences'], $contextData['contexts'], $evolutionData['patterns'], $fusionData['profiles']);
        $explainData = $this->buildExplainableChairman($strategyData['strategies'], $experienceData['experiences'], $contextData['contexts'], $fusionData['profiles']);
        $simulationData = $this->buildDecisionSimulator($strategyData['strategies']);
        $counterfactualData = $this->buildCounterfactualAI($simulationData['simulations']);
        $debateData = $this->buildSelfDebate($strategyData['strategies'], $counterfactualData['counterfactuals']);
        $stabilizerData = $this->buildDebateConsensusStabilizer($debateData['debates']);
        $stressData = $this->buildScenarioStressTest($simulationData['simulations'], $stabilizerData['items']);
        $gateData = $this->buildChairmanDecisionGate($stabilizerData['items'], $stressData['items']);
        $auditData = $this->buildDecisionAudit($gateData['items'], $stabilizerData['items'], $stressData['items']);
        $safetyData = $this->buildSafetyGovernor($gateData['items'], $auditData['items'], $stressData['items']);
        $rankingData = $this->buildFinalChairmanRanking($safetyData['items'], $gateData['items'], $strategyData['strategies']);
        $dashboardData = $this->buildUltimateDashboard($rankingData['items'], $safetyData['items'], $auditData['items']);
        $releaseData = $this->buildFinalRelease($dashboardData['summary'], $auditData['summary'], $safetyData['summary'], $rankingData['summary']);
        return array(
            'status'=>'OK','engine'=>'V53','engine_version'=>'54.9.0','race_date'=>$date,'venue'=>$venue,
            'source_race_date'=>isset($v52['source_race_date'])?$v52['source_race_date']:$date,
            'source_v50_run'=>isset($v52['source_v50_run'])?$v52['source_v50_run']:'',
            'source_v51_version'=>isset($v52['source_v51_version'])?$v52['source_v51_version']:'',
            'source_v52_version'=>isset($v52['engine_version'])?$v52['engine_version']:'52.9.0',
            'memory_state'=>$state,'history_snapshot_count'=>count($history),'learning_mode'=>'ADVISORY_ONLY',
            'live_weights_changed'=>false,'pre_race_decisions_immutable'=>true,'database_schema_changed'=>false,
            'memory_summary'=>array('memory_record_count'=>count($records),'positive_memory_count'=>$positive,'negative_memory_count'=>$negative,'neutral_memory_count'=>$neutral,'promote_count'=>$promote,'guard_count'=>$guard,'caution_count'=>$caution,'average_memory_strength'=>round($avgStrength,2),'memory_state'=>$state,'top_positive_memory'=>isset($topPositive[0])?$topPositive[0]['memory_key']:'NONE','top_negative_memory'=>isset($topNegative[0])?$topNegative[0]['memory_key']:'NONE'),
            'memory_votes'=>array('positive'=>array_slice($topPositive,0,8),'negative'=>array_slice($topNegative,0,8)),
            'memory_records'=>$records,
            'pattern_cluster_summary'=>$clusterData['summary'],
            'pattern_clusters'=>$clusterData['clusters'],
            'pattern_cluster_mapping'=>$clusterData['mapping'],
            'relationship_graph_summary'=>$graphData['summary'],
            'relationship_graph_nodes'=>$graphData['nodes'],
            'relationship_graph_edges'=>$graphData['edges'],
            'relationship_graph_node_metrics'=>$graphData['node_metrics'],
            'experience_summary'=>$experienceData['summary'],
            'chairman_experiences'=>$experienceData['experiences'],
            'context_awareness_summary'=>$contextData['summary'],
            'context_profiles'=>$contextData['contexts'],
            'pattern_evolution_summary'=>$evolutionData['summary'],
            'pattern_evolution'=>$evolutionData['patterns'],
            'multi_race_fusion_summary'=>$fusionData['summary'],
            'multi_race_fusion_profiles'=>$fusionData['profiles'],
            'strategy_optimizer_summary'=>$strategyData['summary'],
            'chairman_strategies'=>$strategyData['strategies'],
            'explainable_ai_summary'=>$explainData['summary'],
            'chairman_explanations'=>$explainData['explanations'],
            'decision_simulator_summary'=>$simulationData['summary'],
            'decision_simulations'=>$simulationData['simulations'],
            'counterfactual_ai_summary'=>$counterfactualData['summary'],
            'counterfactual_analysis'=>$counterfactualData['counterfactuals'],
            'self_debate_summary'=>$debateData['summary'],
            'chairman_debates'=>$debateData['debates'],
            'debate_stabilizer_summary'=>$stabilizerData['summary'],
            'debate_stabilizers'=>$stabilizerData['items'],
            'stress_test_summary'=>$stressData['summary'],
            'scenario_stress_tests'=>$stressData['items'],
            'decision_gate_summary'=>$gateData['summary'],
            'chairman_decision_gates'=>$gateData['items'],
            'decision_audit_summary'=>$auditData['summary'],
            'decision_audits'=>$auditData['items'],
            'safety_governor_summary'=>$safetyData['summary'],
            'safety_governor'=>$safetyData['items'],
            'final_ranking_summary'=>$rankingData['summary'],
            'final_chairman_ranking'=>$rankingData['items'],
            'ultimate_dashboard_summary'=>$dashboardData['summary'],
            'ultimate_dashboard'=>$dashboardData['items'],
            'final_release'=>$releaseData,
            'source_v52_release'=>isset($v52['ultimate_release_readiness'])?$v52['ultimate_release_readiness']:array()
        );
    }
    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;
    }
}
