<?php
declare(strict_types=1);
date_default_timezone_set('Asia/Shanghai');require __DIR__.'/contract.php';$db=smPdo();$fixture=smLoad('fixture-private.json');$pre=smLoad('preflight-private.json');$actor=smLoad('actor-private.json');$seed=$fixture['seed'];$ids=$fixture['ids'];$runtime=getenv('SM7_RUNTIME');$root=getenv('SM7_ROOT');$base=getenv('SM7_BASE');$lane=getenv('SM7_LANE');
if(is_file($runtime.'/run-intent-private.json')||$fixture['meal_updates']!==0){throw new RuntimeException('Do not replay fixture updates');}
foreach($pre['source_sha256'] as $f=>$sha){if(hash_file('sha256',$root.'/'.$f)!==$sha){throw new RuntimeException('Frozen dependency source changed');}}
$own=hcOwnRows($db,$seed,$ids);if(smDigest($own)!==smDigest($fixture['own_rows'])){throw new RuntimeException('Owned fixture drift before HTTP');}
$boundary=json_decode(file_get_contents($base.'/__poc_boundary'),true);if(empty($boundary['private_source_reflection'])||empty($boundary['file_cache'])||empty($boundary['redis_disabled'])||empty($boundary['external_stubs'])||($boundary['real_queue']??true)!==false){throw new RuntimeException('Private boundary');}
require $root.'/application/base/Security.php';$token=\app\base\Security::encrypt(implode("\t",[(string)$actor['id'],$actor['uuid'],$actor['marker'],'0','','',$actor['marker']]));
$strict=static function($a,$b,string $p='$')use(&$strict):?string{if(gettype($a)!==gettype($b)){return $p.':type';}if(is_object($a)){$a=get_object_vars($a);$b=get_object_vars($b);ksort($a);ksort($b);}if(is_array($a)){if(array_keys($a)!==array_keys($b)){return $p.':keys';}foreach($a as $k=>$v){if(($e=$strict($v,$b[$k],$p.'.'.$k))!==null){return $e;}}return null;}return $a===$b?null:$p.':value';};
$cases=[];$responses=[];$sources=[];$updates=[];$error=null;$expectedMeal=4;$state=['phase'=>'start','updates'=>[]];smSave('run-intent-private.json',$state);
$readProof=static function(bool $hardware,string $search='')use($db,$seed,$ids):array{
    $rows=hcOwnRows($db,$seed,$ids);$m=$rows['initial_menu'];$staff=$rows['staff'];
    if($hardware){
        $img=$rows['date_menu_hardware_img'];$recipe=smRows($db,'SELECT id FROM ydy_recipes WHERE id=? AND uuid=? AND dishes_uuid=? AND menu_date=? AND meal_times=?',[$ids['recipes'],$seed['recipes'],$seed['dishes'],$seed['date'],'1']);$logs=smRows($db,'SELECT id FROM ydy_initial_menu_log WHERE staff_uuid=? AND menu_date=? AND meal_times=? AND user_type=1 AND terminal=1',[$seed['staff'],$seed['date'],'1']);
        $dish=['hardware_img_uuid'=>'','recommend_uuid'=>'','date_menu_uuid'=>$m['uuid'],'select_adjust'=>empty($m['select_adjust'])?'':(string)$m['select_adjust'],'dishes_uuid'=>$m['dishes_uuid'],'code'=>$m['code'],'name'=>$rows['dishes']['name'],'weight'=>$m['weight'],'in_recipes'=>$recipe?1:0,'un_edit'=>(!$m['weight']&&!$rows['dishes']['name'])?0:1];
        $data=[['id'=>$img['id'],'uuid'=>$img['uuid'],'menu_date'=>$img['menu_date'],'meal_times'=>$img['meal_times'],'img'=>$img['img'],'dishes'=>[$dish,[]]]];$body=['code'=>0,'message'=>!$logs,'data'=>$data];
    }else{
        // Independently check the exact name predicate via bound source SQL; no ORM/getter.
        $args=[$seed['staff']];$sql='SELECT uuid,name,head_img,sex FROM ydy_staff WHERE uuid=?';if($search!==''){$sql.=' AND name LIKE ?';$args[]='%'.$search.'%';}$matches=smRows($db,$sql,$args);
        $data=[];if($matches){$r=$matches[0];$data[]=['staff_uuid'=>$r['uuid'],'head_img'=>'','staff_name'=>$r['name'],'sex'=>$r['sex'],'meal_times'=>$m['meal_times'],'is_flag'=>(int)$m['meal_type']===4];}
        $body=['code'=>0,'message'=>'查询成功','data'=>['rows'=>$data,'total'=>count($data)]];
    }
    return ['body'=>json_decode(json_encode($body,JSON_UNESCAPED_UNICODE|JSON_THROW_ON_ERROR)),'own_rows'=>$rows,'meal_type'=>(int)$m['meal_type']];
};
$request=static function(string $name,bool $hardware,string $search='')use($base,$token,$seed,$db,$ids,$fixture,$pre,$actor,$readProof,$strict,&$expectedMeal,&$cases,&$responses,&$sources):void{
    $expectedOwn=$fixture['own_rows'];$expectedOwn['initial_menu']['meal_type']=$expectedMeal;if(smDigest(hcOwnRows($db,$seed,$ids))!==smDigest($expectedOwn)){throw new RuntimeException('Owned consumed rows changed before request');}
    $consumedBefore=hcConsumedSource($db,$seed,$ids,$pre,$actor,$expectedMeal);
    $path=$hardware?'/p/date_menu/hardwareInfo':'/p/dateMenu/checkMenuList';$params=$hardware?['staff_uuid'=>$seed['staff'],'menu_date'=>$seed['date'],'meal_times'=>'1','type'=>1]:['department_uuid'=>$seed['department'],'date'=>$seed['date'],'meal_times'=>'1','page'=>1,'page_size'=>5,'staff_name'=>$search];
    $opts=['method'=>'POST','header'=>"Host: cdx-correction.invalid\r\nContent-Type: application/x-www-form-urlencoded;charset=UTF-8\r\ntoken: ".$token."\r\nX-Pilot-Scenario: PILOT_HC_".$name."\r\n",'content'=>http_build_query($params),'ignore_errors'=>true,'follow_location'=>0,'timeout'=>25];$raw=file_get_contents($base.$path,false,stream_context_create(['http'=>$opts]));preg_match('~^HTTP/\S+ (\d+)~',$http_response_header[0]??'',$m);$status=(int)($m[1]??0);$body=json_decode((string)$raw,false,512,JSON_THROW_ON_ERROR);$proof=$readProof($hardware,$search);$difference=$strict($proof['body'],$body);$pass=$status===200&&$difference===null;
    if(smDigest($proof['own_rows'])!==smDigest($expectedOwn)){throw new RuntimeException('Owned consumed rows changed during request');}
    $consumedAfter=hcConsumedSource($db,$seed,$ids,$pre,$actor,$expectedMeal);if($consumedBefore!==$consumedAfter){throw new RuntimeException('Consumed relation window changed');}
    $proof['consumed_before']=$consumedBefore;$proof['consumed_after']=$consumedAfter;
    $responses[$name]=['method'=>'POST','path'=>$path,'http_status'=>$status,'body'=>$body,'raw_wire'=>(string)$raw,'wire_bytes'=>strlen((string)$raw),'wire_sha256'=>hash('sha256',(string)$raw)];$sources[$name]=$proof;$cases[]=['case'=>$name,'method'=>'POST','path'=>$path,'http_status'=>$status,'code'=>$body->code??null,'source_pass'=>$pass,'difference'=>$difference,'meal_type'=>$proof['meal_type'],'expected_nonempty'=>$hardware||count($proof['body']->data->rows)>0,'owned_request_window_unchanged'=>true,'consumed_relation_window_unchanged'=>true];
};
$update=static function(int $from,int $to)use($db,$seed,$ids,&$expectedMeal,&$updates,&$state):void{
    $state['phase']='pending-meal-'.$from.'-'.$to;smSave('run-intent-private.json',$state);
    $s=$db->prepare('UPDATE ydy_initial_menu SET meal_type=? WHERE id=? AND uuid=? AND staff_uuid=? AND hardware_img_uuid=? AND menu_date=? AND meal_times=? AND create_by=? AND meal_type=?');$s->execute([$to,$ids['initial_menu'],$seed['menu'],$seed['staff'],$seed['image'],$seed['date'],'1',$seed['marker'],$from]);if($s->rowCount()!==1){throw new RuntimeException('Exact owned meal transition failed');}
    $r=hcOwnRows($db,$seed,$ids);if((int)$r['initial_menu']['meal_type']!==$to){throw new RuntimeException('Owned transition readback');}
    $expectedMeal=$to;$updates[]=['from'=>$from,'to'=>$to,'rows'=>1,'owned_pk'=>$ids['initial_menu']];$state['updates']=$updates;$state['phase']='meal-'.$to;smSave('run-intent-private.json',$state);
};
try{
    $request('HARDWARE4',true);$request('CHECK4',false);$request('SEARCH4',false,$own['staff']['name']);$request('PREFIX4',false,$seed['marker']);
    $update(4,1);$request('HARDWARE_FALLBACK1',true);$request('CHECK1',false);$update(1,4);$request('RESTORED_CHECK4',false);
}catch(Throwable $e){$error=['class'=>get_class($e),'message_sha256'=>hash('sha256',$e->getMessage())];smSave('run-error-private.json',['message'=>$e->getMessage()]);}
if(count($updates)===1&&$updates[0]['to']===1){try{$update(1,4);}catch(Throwable $e){$error=['restore_failure'=>hash('sha256',$e->getMessage())];}}
$final=hcOwnRows($db,$seed,$ids);$restored=smDigest($final)===smDigest($fixture['own_rows']);$scopedStaff=$lane==='B'&&!empty($fixture['root_authorized_staff_concurrency_scope']);$natural=hcSnapshot($db,$seed,$ids,$scopedStaff);$expectedNatural=$pre['before'];$globalCount=['before'=>$expectedNatural['initial_menu_global_count'],'after'=>$natural['initial_menu_global_count'],'responsibility'=>'unknown','acceptance_scope'=>!$scopedStaff];if($scopedStaff){unset($expectedNatural['staff'],$expectedNatural['initial_menu_global_count'],$natural['initial_menu_global_count']);}$actorNow=smRows($db,'SELECT * FROM ydy_user WHERE id=? AND uuid=? AND account=? AND create_by=?',[$actor['id'],$actor['uuid'],$actor['marker'],$actor['marker']]);$rest=smRows($db,'SELECT * FROM ydy_restaurants WHERE id=? AND uuid=? AND name=?',[$pre['restaurant_id'],$pre['restaurant_uuid'],$pre['restaurant_marker']]);
$events=[];foreach(file($runtime.'/queries.jsonl',FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)?:[] as $line){$events[]=json_decode($line,true);}$httpDml=0;foreach($events as $e){if($e['kind']==='query_observed'&&in_array($e['operation'],['insert','update','delete','replace','merge'],true)){$httpDml++;}}
$consumedScope=!empty($fixture['root_authorized_consumed_scope']);$consumedPass=!$error&&count($cases)===7&&count(array_filter($cases,static function($c){return !empty($c['consumed_relation_window_unchanged']);}))===7;
$report=['lane'=>$lane,'cases'=>$cases,'unexpected'=>$error,'updates'=>$updates,'fixture_restored'=>$restored,'natural_before'=>$expectedNatural,'natural_after_excluding_owned'=>$natural,'natural_unchanged'=>$expectedNatural===$natural,'global_natural_unchanged_proven'=>!$scopedStaff&&$expectedNatural===$natural,'consumed_source_unchanged'=>$consumedPass,'root_authorized_consumed_scope'=>$consumedScope,'natural_scope'=>$scopedStaff?'consumed own GUID/date/meal relation sets; global staff/relation/menu-count concurrent changes unknown, not acceptance gates':'seven-tables; initial_menu date-scope plus total','natural_staff_fulltable_unchanged'=>!$scopedStaff,'initial_menu_global_count_telemetry'=>$globalCount,'actor_unchanged'=>smDigest($actorNow)===$pre['actor_digest'],'restaurant_unchanged'=>smDigest($rest)===$pre['restaurant_digest'],'http_dml'=>$httpDml,'boundary'=>$boundary,'events'=>$events,'source_sha256'=>$pre['source_sha256'],'retained_ids'=>$ids,'marker'=>$seed['marker']];
$state['phase']='finished-restored';$state['source_failures']=count(array_filter($cases,static function($c){return !$c['source_pass'];}));smSave('run-intent-private.json',$state);smSave('responses-private.json',$responses);smSave('source-private.json',$sources);smSave('report-private.json',$report);$fixture['meal_updates']=count($updates);$fixture['own_rows_final']=$final;smSave('fixture-private.json',$fixture);
echo json_encode(['lane'=>$lane,'cases'=>$cases,'updates'=>$updates,'restored'=>$restored,'natural_unchanged'=>$report['natural_unchanged'],'consumed_source_unchanged'=>$consumedPass,'http_dml'=>$httpDml,'unexpected'=>$error]),PHP_EOL;exit($error||!$restored||($consumedScope?!$consumedPass:!$report['natural_unchanged'])||!$report['actor_unchanged']||!$report['restaurant_unchanged']||$httpDml?1:0);
