<?php
// Pure recovery tests against the actual stopped schema; no config/PDO/SQL.
require __DIR__.'/recover.php';
$p=json_decode(file_get_contents(__DIR__.'/recovery-plan.json'),true);$n=0;$badCount=0;
$ok=static function($v,$s)use(&$n){++$n;if(!$v)throw new RuntimeException($s);};
$deny=static function($f,$s)use($ok,&$badCount){try{$f();}catch(Throwable $e){++$badCount;$ok(true,$s);return;}$ok(false,'Not rejected '.$s);};
$ok(pc_gate_plan($p)===['read'=>122,'DML'=>28,'DDL'=>4],'Exact resume budget');
$ok($p['cumulative']['read']===142&&$p['cumulative']['DDL']===5&&$p['fresh_read_count']===10,'Exact initial plus resume budget');
$ref=pc_reference();$schema=pc_original_schema($ref);pc_schema($schema,6,false,$p);$ok(true,'Actual DM cluster plus PK index fixture accepted');
foreach(['cluster_owner','unknown_cluster','PK_pointer','PK_not_unique','fsp','nullability','extra_index','unknown_trigger']as $kind){$s=$schema;if($kind==='cluster_owner')$s['indexes'][0]['table_name']='YDY_USER';if($kind==='unknown_cluster')$s['indexes'][0]['index_name']='UNKNOWN';if($kind==='PK_pointer')$s['constraints'][0]['index_name']='INDEX33556697';if($kind==='PK_not_unique')$s['indexes'][1]['uniqueness']='NONUNIQUE';if($kind==='fsp')$s['columns'][9]['data_scale']=0.0;if($kind==='nullability')$s['columns'][6]['nullable']='N';if($kind==='extra_index')$s['indexes'][]=$s['indexes'][0];if($kind==='unknown_trigger')$s['triggers'][]=['trigger_name'=>'UNKNOWN'];$deny(static function()use($s,$p){pc_schema($s,6,false,$p);},$kind);}
foreach($p['steps']as $i=>$s){$state=['cursor'=>$i,'read'=>0,'DML'=>0,'DDL'=>0,'created'=>true,'trigger_created'=>true,'collision_zero'=>true,'drop_proof'=>true,'table_drop_proof'=>true];if($s['role']==='create_trigger')$state['trigger_created']=false;pc_gate_step($p,$s,$state);$ok(true,'Literal resume step');}
foreach(['CREATE_replay','remove_fresh','budget','foreign_bind','unknown_drop','failure_SHA','wrong_SQL_type']as $kind){$b=$p;if($kind==='CREATE_replay')array_unshift($b['steps'],['kind'=>'DDL','SQL'=>$ref['create']]);if($kind==='remove_fresh')array_shift($b['steps']);if($kind==='budget')$b['cumulative']['read']=181;if($kind==='foreign_bind')$b['steps'][array_search('CAS4',array_column($b['steps'],'label'))]['binds'][4]=2;if($kind==='unknown_drop')$b['steps'][array_search('drop_table',array_column($b['steps'],'role'))]['SQL']='DROP TABLE ZHCT_APP.YDY_USER';if($kind==='failure_SHA')$b['initial_failure_sha256']=str_repeat('0',64);if($kind==='wrong_SQL_type')$b['steps'][array_search('CAS4',array_column($b['steps'],'label'))]['PDO_types'][0]=2;$deny(static function()use($b){pc_gate_plan($b);},$kind);}
foreach(['drop_trigger','drop_table']as $role){$i=array_search($role,array_column($p['steps'],'role'));$s=$p['steps'][$i];$st=['cursor'=>$i,'read'=>0,'DML'=>0,'DDL'=>0,'created'=>true,'trigger_created'=>true,'collision_zero'=>true,'drop_proof'=>false,'table_drop_proof'=>false];$deny(static function()use($p,$s,$st){pc_gate_step($p,$s,$st);},'Unknown cleanup proof');}
$ok(!in_array($ref['create'],array_column($p['steps'],'SQL'),true),'No successful CREATE can be resent');
$ok(array_column(array_slice($p['steps'],0,10),'role')===array_fill(0,10,'recovery_fresh'),'Ten fresh readonly gates first');
echo json_encode(['status'=>'PURE_RECOVERY_GATES_PASS','checks'=>$n,'counterexamples'=>$badCount,'resume_read'=>122,'cumulative_read'=>142,'resume_DML'=>28,'resume_DDL'=>4,'cumulative_DDL'=>5,'DB_connections'=>0,'SQL'=>0,'HTTP'=>0]),"\n";
