<?php
declare(strict_types=1);
date_default_timezone_set('Asia/Shanghai');require __DIR__.'/common.php';require __DIR__.'/projection.php';
$lane=getenv('PILOT_LANE');$root=getenv('ROLE_MENU_APP_ROOT');$runtime=getenv('PILOT_RUNTIME');$base=getenv('POC_BASE_URL');
if(!in_array($base,['http://127.0.0.1:18371','http://127.0.0.1:18372'],true)){exit(78);}
$pre=json_decode(file_get_contents($runtime.'/preflight-private.json'),true);$tenant=$pre['tenant'];$db=cpPdo();$token=bin2hex(random_bytes(24));$app=cpBootstrap($root,$runtime);
\think\facade\Cache::set($token,['user'=>['store_id'=>$tenant,'store_user_id'=>1930510010,'real_name'=>'CDXRM10','user_name'=>'CDXRM10','is_super'=>1],'store_id'=>$tenant,'is_login'=>true],3600);
file_put_contents($runtime.'/fixture_map.json',json_encode(['tenant'=>$tenant,'token'=>$token]));chmod($runtime.'/fixture_map.json',0600);
$cases=[];$responses=[];$source=[];$error=null;
try{
    $boundary=json_decode(file_get_contents($base.'/__poc_boundary'));if($boundary!==null&&in_array(false,get_object_vars($boundary),true)){throw new RuntimeException('Private boundary failed');}
    foreach(['store','terminal'] as $m){
        $scenarios=[['MENU_COLD','Menu',null],['MENU_REPEAT','Menu',null],['ROLE_ALL','Role',null],['ROLE_MATCH','Role',$pre['root_role_name']??'CDXRM10_MISSING'],['ROLE_QUOTE','Role',"中文'安全检索"],['ROLE_MISSING','Role','CDXRM10_NO_SUCH_ROLE_6E24']];
        foreach($scenarios as [$tag,$controller,$search]){
            $name=($m==='store'?'S':'T').'_'.$tag;$path='/'.$m.'/store.'.$controller.'/list';$params=['storeId'=>$tenant];if($search!==null){$params['search']=$search;}
            $opts=['method'=>'GET','header'=>"Host: cdx-role-menu.invalid\r\nAccess-Token: ".$token."\r\nX-Pilot-Scenario: PILOT_RM_".$name."\r\n",'ignore_errors'=>true,'follow_location'=>0,'timeout'=>25];$raw=file_get_contents($base.$path.'?'.http_build_query($params),false,stream_context_create(['http'=>$opts]));preg_match('~^HTTP/\S+ (\d+)~',$http_response_header[0]??'',$mt);$body=json_decode((string)$raw);$status=(int)($mt[1]??0);
            $responses[$name]=['method'=>'GET','path'=>$path,'http_status'=>$status,'body'=>$body];$proof=$controller==='Menu'?rmMenuProjection($db):rmRoleProjection($db,$tenant,$search);$expected=json_decode(json_encode($proof['expected'],JSON_UNESCAPED_UNICODE|JSON_PRESERVE_ZERO_FRACTION|JSON_THROW_ON_ERROR));
            $actual=$body->data->list??null;$difference=rmStrict($expected,$actual);$pass=$status===200&&($body->status??null)===200&&$difference===null;$source[$name]=$proof;
            $cases[]=['case'=>$name,'method'=>'GET','path'=>$path,'http_status'=>$status,'status'=>$body->status??null,'source_pass'=>$pass,'source_nodes'=>count($proof['source_rows']),'root_nodes'=>is_array($actual)?count($actual):null,'difference'=>$difference];
        }
    }
}catch(Throwable $e){$error=['class'=>get_class($e),'file'=>basename($e->getFile()),'line'=>$e->getLine(),'message_sha256'=>hash('sha256',$e->getMessage())];}
\think\facade\Cache::delete($token);$tokenCleaned=!\think\facade\Cache::has($token);\think\facade\Cache::clear();$after=[];foreach(['store_role','store_role_menu','store_menu','store_menu_api'] as $t){$after[$t]=cpDigest(cpRows($db,'SELECT * FROM yoshop_'.$t));}
$events=[];foreach(file($runtime.'/queries.jsonl',FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)?:[] as $line){$events[]=json_decode($line,true);}$sha=[];foreach(file(__DIR__.'/SOURCE_SHA256.txt',FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES) as $line){[$hash,$f]=preg_split('/\s+/',$line,2);$sha[$f]=hash_file('sha256',$root.'/'.$f);if($sha[$f]!==$hash){$error=['source_sha_drift'=>$f];}}
$report=['lane'=>$lane,'cases'=>$cases,'unexpected'=>$error,'boundary'=>$boundary??null,'before'=>$pre['before'],'after'=>$after,'natural_unchanged'=>$pre['before']===$after,'token_cleaned'=>$tokenCleaned,'external_attempts'=>is_file($runtime.'/external-private.jsonl')?count(file($runtime.'/external-private.jsonl')):0,'source_sha256'=>$sha,'events'=>$events,'dml'=>0,'ddl'=>0];
foreach(['report.json'=>$report,'responses-private.json'=>$responses,'source-private.json'=>$source,'fixture_map.json'=>['tenant'=>$tenant]] as $f=>$v){file_put_contents($runtime.'/'.$f,json_encode($v,JSON_UNESCAPED_UNICODE|JSON_PRESERVE_ZERO_FRACTION|JSON_THROW_ON_ERROR));chmod($runtime.'/'.$f,0600);}
echo json_encode(['lane'=>$lane,'cases'=>count($cases),'source_failures'=>count(array_filter($cases,static function($c){return !$c['source_pass'];})),'unexpected'=>$error!==null,'natural_unchanged'=>$report['natural_unchanged'],'cases_summary'=>$cases]),PHP_EOL;
exit($error!==null||!$report['natural_unchanged']||!$tokenCleaned?1:0);
