<?php
declare(strict_types=1);
$lane=getenv('DCW_LANE');$root=getenv('DCW_ROOT');$marker=getenv('DCW_MARKER');$mode=$argv[1]??'';$out=['lane'=>$lane,'mode'=>$mode,'routes'=>[]];$stage='config';
function r(PDO $db,string $sql,array $args=[]):array{if(!preg_match('/^SELECT\b/i',$sql))throw new RuntimeException('SELECT only');$q=$db->prepare($sql);$q->execute($args);$rows=[];while($v=$q->fetch(PDO::FETCH_ASSOC)){if(count($rows)>2000)throw new RuntimeException('Source cap');$v=array_change_key_case($v,CASE_LOWER);foreach($v as &$x)if(is_resource($x))$x=stream_get_contents($x);ksort($v);$rows[]=$v;}return $rows;}
function h($v){return hash('sha256',json_encode($v,JSON_UNESCAPED_UNICODE|JSON_INVALID_UTF8_SUBSTITUTE));}
function ws($v){if(is_object($v)){$a=[];foreach(get_object_vars($v) as $k=>$x)$a[$k]=ws($x);ksort($a);return ['kind'=>'object','fields'=>$a];}if(is_array($v)){$a=[];foreach($v as $x){$s=ws($x);$a[h($s)]=$s;}ksort($a);return ['kind'=>'array','element_shapes'=>array_values($a)];}return is_int($v)||is_float($v)?'number':gettype($v);}
function createdTimeEvidence(array $row):array{$a=(string)$row['create_time'];$b=(string)$row['update_time'];$pattern='/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.0+)?$/D';return ['same_raw_value'=>$a===$b,'create_no_nonzero_fraction'=>(bool)preg_match($pattern,$a),'update_no_nonzero_fraction'=>(bool)preg_match($pattern,$b),'create_wire_length'=>strlen($a),'update_wire_length'=>strlen($b)];}
try{
 if(!in_array($lane,['B','C'],true)||!in_array($mode,['preflight','execute','audit','reads'],true)||!preg_match('/^CDXDCW10_[A-F0-9]{8}$/D',$marker))throw new RuntimeException('Invocation');
 $cfg=require $root.'/application/database.php';if($lane==='B'){$local=require '/workspace/zhct/zhctproject/store/application/config/local/database.php';foreach(['hostname','database','username','password'] as $k)if(($cfg[$k]??null)!==($local[$k]??null))throw new RuntimeException('Not local');$dsn='mysql:host='.$cfg['hostname'].';port='.($cfg['hostport']?:3306).';dbname='.$cfg['database'].';charset=utf8mb4';}
 else{foreach(['hostname'=>'POC_HOST','hostport'=>'POC_PORT','database'=>'POC_SCHEMA','username'=>'POC_USER','password'=>'POC_PASSWORD'] as $k=>$e)if((string)$cfg[$k]!==getenv($e))throw new RuntimeException('DM config');if(getenv('POC_HOST')!=='127.0.0.1'||getenv('POC_PORT')!=='5237'||getenv('POC_SCHEMA')!=='ZHCT_APP')throw new RuntimeException('DM identity');$dsn='dm:host=127.0.0.1;port=5237;schema=ZHCT_APP';}
 $db=new PDO($dsn,$cfg['username'],$cfg['password'],[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_CASE=>PDO::CASE_LOWER]);$p=$lane==='B'?'ydy_':'YDY_';$base='http://127.0.0.1:'.($lane==='B'?18301:18302);
 $stage='boundary';$boundary=json_decode((string)file_get_contents($base.'/__poc_boundary'),true);if(!is_array($boundary)||($boundary['school_feature']??true)!==false||strtolower((string)($boundary['business_constant']??''))==='zhct_stzc'||($boundary['external_stubs']??false)!==true)throw new RuntimeException('Original boundary unsupported');$out['boundary']=$boundary;
 $stage='source-integrity';foreach(['api/controller/DiningConfig.php'=>['746a4af1a6fbe55615f82b2102e533b4b11afcd70007f40eaaa7a4c2b8aef6db','c80749aa80aff6b0f38651ae2d0888d2db117b507a643a19b4a2d57cc8497d81'],'api/service/DiningConfig.php'=>['a3734fbeef3766b66c60d671000911f5f326c38126dda7b90833aa8e54e83015','2249fd514d147613fb8ab8b1c238f1934fe69f0da4b5bb902570cafeb8bff1c8','4cbc230c468932e78c1f30cc93938b5fd64736536ba98becba0a0f80e52bc5fc'],'api/validate/DiningConfig.php'=>['be0c2f3023bdbcb5c885e658b66b1c4743f40406ba7fabea25538bb19b3c5d12']] as $file=>$allowedHashes){$sha=hash_file('sha256',__DIR__.'/source/application/'.$file);if(!in_array($sha,$allowedHashes,true))throw new RuntimeException('Source drift');$out['source_sha256'][$file]=$sha;}
 if($lane==='C'){$clock=r($db,"SELECT TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS') AS TS FROM DUAL")[0]['ts'];$out['dm_clock_expression']=['type'=>gettype($clock),'length'=>strlen($clock),'seconds_format'=>(bool)preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/D',$clock)];if(!$out['dm_clock_expression']['seconds_format'])throw new RuntimeException('DM clock expression');}
 if($lane==='C'){$nativeEqual=0;$secondsEqual=0;for($i=0;$i<20;$i++){$t=r($db,"SELECT CURRENT_TIMESTAMP AS T1,CURRENT_TIMESTAMP AS T2,TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS') AS S1,TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS') AS S2 FROM DUAL")[0];if($t['t1']===$t['t2'])$nativeEqual++;if($t['s1']===$t['s2'])$secondsEqual++;}$out['dm_statement_clock_probe']=['samples'=>20,'native_equal_samples'=>$nativeEqual,'seconds_equal_samples'=>$secondsEqual];if($secondsEqual!==20)throw new RuntimeException('Statement clock seconds differed');}
 $stage='schema';if($lane==='B')$out['server_version']=$db->query('SELECT VERSION()')->fetchColumn();$tables=[$p.'restaurant_area',$p.'restaurant_shop'];foreach($tables as $table){$sql=$lane==='B'?'SELECT COLUMN_NAME,DATA_TYPE,IS_NULLABLE,COLUMN_DEFAULT,COLUMN_KEY,EXTRA,DATETIME_PRECISION FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=? ORDER BY ORDINAL_POSITION':'SELECT COLUMN_NAME,DATA_TYPE,NULLABLE,DATA_DEFAULT,DATA_SCALE AS DATETIME_PRECISION FROM USER_TAB_COLUMNS WHERE TABLE_NAME=? ORDER BY COLUMN_ID';$out['schema'][$table]=r($db,$sql,[$table]);}
 $stage='triggers';$triggers=$lane==='B'?r($db,'SELECT TRIGGER_NAME,EVENT_OBJECT_TABLE,ACTION_TIMING,EVENT_MANIPULATION FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA=DATABASE() AND EVENT_OBJECT_TABLE IN (?,?)',$tables):r($db,'SELECT TRIGGER_NAME,TABLE_NAME,STATUS FROM USER_TRIGGERS WHERE TABLE_NAME IN (?,?)',$tables);$out['trigger_count']=count($triggers);if($triggers)throw new RuntimeException('Trigger review required');
 $stage='foreign-keys';$fk=$lane==='B'?r($db,'SELECT TABLE_NAME,COLUMN_NAME,REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA=DATABASE() AND REFERENCED_TABLE_NAME IS NOT NULL AND (TABLE_NAME IN (?,?) OR REFERENCED_TABLE_NAME IN (?,?))',array_merge($tables,$tables)):r($db,"SELECT C.TABLE_NAME,C.CONSTRAINT_NAME,R.TABLE_NAME AS REFERENCED_TABLE_NAME FROM USER_CONSTRAINTS C JOIN USER_CONSTRAINTS R ON C.R_CONSTRAINT_NAME=R.CONSTRAINT_NAME WHERE C.CONSTRAINT_TYPE='R' AND (C.TABLE_NAME IN (?,?) OR R.TABLE_NAME IN (?,?))",array_merge($tables,$tables));$out['foreign_key_count']=count($fk);if($fk)throw new RuntimeException('Foreign-key review required');
 $stage='natural-baseline';$natural=['areas'=>r($db,'SELECT * FROM '.$p.'restaurant_area ORDER BY ID'),'shops'=>r($db,'SELECT * FROM '.$p.'restaurant_shop ORDER BY ID'),'stalls'=>r($db,'SELECT * FROM '.$p.'restaurants ORDER BY ID')];$out['natural_before']=h($natural);$out['natural_counts']=array_map('count',$natural);
 $ownArea=r($db,'SELECT ID FROM '.$p.'restaurant_area WHERE NAME LIKE ?',[$marker.'%']);$ownShop=r($db,'SELECT ID FROM '.$p.'restaurant_shop WHERE NAME LIKE ?',[$marker.'%']);$out['own_before']=['areas'=>count($ownArea),'shops'=>count($ownShop)];if($mode==='audit'){echo json_encode($out),"\n";exit;}
 if($ownArea||$ownShop)throw new RuntimeException('Own marker already present');
 $orphans=[(int)r($db,'SELECT COUNT(*) AS N FROM '.$p.'restaurant_shop S LEFT JOIN '.$p.'restaurant_area A ON A.ID=S.AREA_ID WHERE S.AREA_ID>0 AND A.ID IS NULL')[0]['n'],(int)r($db,'SELECT COUNT(*) AS N FROM '.$p.'restaurants R LEFT JOIN '.$p.'restaurant_shop S ON S.ID=R.RESTAURANT_SHOP_ID WHERE R.RESTAURANT_SHOP_ID>0 AND S.ID IS NULL')[0]['n']];$out['natural_orphan_reference_counts']=$orphans;if($orphans!==[0,0])throw new RuntimeException('Orphan-reference review required');
 $admin=r($db,'SELECT ID,UUID,TYPE FROM '.$p.'user WHERE ID=(SELECT MIN(ID) FROM '.$p.'user WHERE TYPE=0 AND IS_DELETE=0)');if(count($admin)!==1)throw new RuntimeException('Natural admin absent');$admin=$admin[0];$out['preflight_pass']=true;
 if($mode==='reads'){
  require_once $root.'/application/base/Security.php';$token=\app\base\Security::encrypt(implode("\t",[(string)$admin['id'],(string)$admin['uuid'],$marker,'0','','',$marker]));$valid=array_values(array_filter($natural['shops'],static function($x){return (int)$x['is_delete']===0;}));$last=end($valid);if(!$last)throw new RuntimeException('Natural shop absent');
  foreach(['restaurantList'=>['page'=>1,'page_size'=>10],'restaurantDetail'=>['id'=>(int)$last['id']],'restaurantOptions'=>[],'stallOptions'=>[]] as $action=>$params){$ctx=stream_context_create(['http'=>['method'=>'POST','header'=>"Content-Type: application/x-www-form-urlencoded\r\ntoken: ".$token."\r\n",'content'=>http_build_query($params),'timeout'=>30,'ignore_errors'=>true]]);$raw=(string)file_get_contents($base.'/api/dining_config/'.$action,false,$ctx);$j=json_decode($raw,true);$out['routes'][$action]=['path'=>'/api/dining_config/'.$action,'method'=>'POST','status'=>(int)substr($http_response_header[0]??'',9,3),'code'=>$j['code']??null,'wire_sha256'=>hash('sha256',$raw),'wire_shape'=>ws(json_decode($raw))];if(($j['code']??-1)!==0)throw new RuntimeException('Read regression response');}
  $after=['areas'=>r($db,'SELECT * FROM '.$p.'restaurant_area ORDER BY ID'),'shops'=>r($db,'SELECT * FROM '.$p.'restaurant_shop ORDER BY ID'),'stalls'=>r($db,'SELECT * FROM '.$p.'restaurants ORDER BY ID')];$out['natural_unchanged']=h($after)===$out['natural_before'];$out['database_writes']=0;$out['failure']='';echo json_encode($out),"\n";exit;
 }
 if($mode==='execute'){
  if(getenv('DCW_APPROVAL')!=='ROOT_TWO_OWN_TABLES_V1')throw new RuntimeException('Approval guard');
  require_once $root.'/application/base/Security.php';$token=\app\base\Security::encrypt(implode("\t",[(string)$admin['id'],(string)$admin['uuid'],$marker,'0','','',$marker]));
  $areaId=0;$shopId=0;$areaNames=[$marker.'-A',$marker.'-A2'];$shopNames=[$marker.'-S',$marker.'-S2'];$executionFailure='';$cleanupDeletes=0;
  $counts=static function()use($db,$p):array{return ['orders'=>(int)r($db,'SELECT COUNT(*) AS N FROM '.$p.'meal_order')[0]['n'],'equipment'=>(int)r($db,'SELECT COUNT(*) AS N FROM '.$p.'equipment')[0]['n']];};$out['non_target_counts_before']=$counts();
  $ownCounts=static function()use($db,$p,$areaNames,$shopNames):array{return ['areas'=>(int)r($db,'SELECT COUNT(*) AS N FROM '.$p.'restaurant_area WHERE NAME IN (?,?)',$areaNames)[0]['n'],'shops'=>(int)r($db,'SELECT COUNT(*) AS N FROM '.$p.'restaurant_shop WHERE NAME IN (?,?)',$shopNames)[0]['n']];};
  $refs=static function()use($db,$p,&$areaId,&$shopId):array{return ['outside_shops'=>$areaId?(int)r($db,'SELECT COUNT(*) AS N FROM '.$p.'restaurant_shop WHERE AREA_ID=? AND ID<>?',[$areaId,$shopId])[0]['n']:0,'outside_stalls'=>$shopId?(int)r($db,'SELECT COUNT(*) AS N FROM '.$p.'restaurants WHERE RESTAURANT_SHOP_ID=?',[$shopId])[0]['n']:0,'outside_orders'=>$shopId?(int)r($db,'SELECT COUNT(*) AS N FROM '.$p.'meal_order O JOIN '.$p.'restaurants R ON R.ID=O.RESTAURANT_ID WHERE R.RESTAURANT_SHOP_ID=?',[$shopId])[0]['n']:0,'outside_equipment'=>$shopId?(int)r($db,'SELECT COUNT(*) AS N FROM '.$p.'equipment E JOIN '.$p.'restaurants R ON R.ID=E.RESTAURANT_ID WHERE R.RESTAURANT_SHOP_ID=?',[$shopId])[0]['n']:0];};
  $call=static function($name,$action,$params,$expected=0)use(&$out,$base,$token,&$stage):array{$stage=$name;$ctx=stream_context_create(['http'=>['method'=>'POST','header'=>"Content-Type: application/x-www-form-urlencoded\r\ntoken: ".$token."\r\n",'content'=>http_build_query($params),'timeout'=>30,'ignore_errors'=>true]]);$raw=@file_get_contents($base.'/api/dining_config/'.$action,false,$ctx);$j=json_decode((string)$raw,true);$wire=json_decode((string)$raw);$entry=['action'=>$action,'status'=>(int)substr($http_response_header[0]??'',9,3),'code'=>$j['code']??null,'wire_shape'=>ws($wire),'message_sha256'=>hash('sha256',(string)($j['message']??'')),'expected_code'=>$expected];$out['routes'][$name]=$entry;if(!is_array($j)||$entry['status']!==200||($j['code']??null)!==$expected)throw new RuntimeException('HTTP business response mismatch '.$name);return $j;};
  $normalize=static function(array $j)use(&$areaId,&$shopId,$marker,$areaNames,$shopNames,&$out):array{$data=$j['data']??[];if(is_array($data)&&isset($data['name'])){if(strpos($data['name'],$marker)!==0||($data['create_by']??'')!==$marker)throw new RuntimeException('Returned row not owned');if(isset($data['id'])){$id=(int)$data['id'];if(in_array($data['name'],$areaNames,true)&&$id===$areaId)$data['id']='<OWN_AREA>';elseif(in_array($data['name'],$shopNames,true)&&$id===$shopId)$data['id']='<OWN_SHOP>';else throw new RuntimeException('Unknown returned ID');}if(isset($data['area_id'])){if((int)$data['area_id']!==$areaId)throw new RuntimeException('Unknown area mapping');$data['area_id']='<OWN_AREA>';}foreach(['create_time','update_time'] as $field)if(isset($data[$field])){if(is_string($data[$field])&&preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/D',$data[$field]))$data[$field]='<VALID_TIMESTAMP>';elseif(is_string($data[$field])&&preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.(\d+)$/D',$data[$field],$m)){$out['timestamp_wire_differences'][$field]=['type'=>'string','length'=>strlen($data[$field]),'fraction_digits'=>strlen($m[1])];$data[$field]='<INCOMPATIBLE_FRACTIONAL_TIMESTAMP>'; }else throw new RuntimeException('Timestamp wire format');}}return ['code'=>$j['code'],'data'=>$data,'message_sha256'=>hash('sha256',$j['message'])];};
  $readOwn=static function($kind,$expected)use($db,$p,&$areaId,&$shopId,$marker):array{$id=$kind==='area'?$areaId:$shopId;$table=$p.($kind==='area'?'restaurant_area':'restaurant_shop');$row=r($db,'SELECT * FROM '.$table.' WHERE ID=? AND CREATE_BY=?',[$id,$marker]);if(count($row)!==1)throw new RuntimeException('Own row identity');$row=$row[0];foreach($expected as $k=>$v){$value=$row[$k]??null;if(in_array($k,['status','order','area_id','is_delete'],true))$value=(int)$value;if($value!==$v)throw new RuntimeException('Own source field '.$k);}return $row;};
  $setOldTime=static function($kind,$literal)use($db,$p,&$areaId,&$shopId,$areaNames,$shopNames,$marker,&$out):string{$id=$kind==='area'?$areaId:$shopId;$table=$p.($kind==='area'?'restaurant_area':'restaurant_shop');$names=$kind==='area'?$areaNames:$shopNames;$q=$db->prepare('UPDATE '.$table.' SET UPDATE_TIME=? WHERE ID=? AND NAME IN (?,?) AND CREATE_BY=?');$q->execute([$literal,$id,$names[0],$names[1],$marker]);$rows=r($db,'SELECT UPDATE_TIME FROM '.$table.' WHERE ID=? AND NAME IN (?,?) AND CREATE_BY=?',[$id,$names[0],$names[1],$marker]);if(count($rows)!==1||substr((string)$rows[0]['update_time'],0,19)!==$literal)throw new RuntimeException('Own timestamp fixture');$out['fixture_timestamp_updates']=($out['fixture_timestamp_updates']??0)+1;return (string)$rows[0]['update_time'];};
  $save=static function($name,array $j)use(&$out,$normalize){$out['routes'][$name]['normalized_own_response']=$normalize($j);$out['routes'][$name]['source_roundtrip']=true;};
  $area=['name'=>$areaNames[0],'status'=>2,'order'=>7,'admin_name'=>'CDX测试','admin_mobile'=>'','intro'=>"CDX '单引号' \\ 数据库兼容",'file_path'=>''];
  try{
   $j=$call('area_create','areaCreate',$area);$identity=r($db,'SELECT ID FROM '.$p.'restaurant_area WHERE NAME=? AND CREATE_BY=?',[$areaNames[0],$marker]);if(count($identity)!==1)throw new RuntimeException('Created area identity');$areaId=(int)$identity[0]['id'];$areaCreated=$readOwn('area',['name'=>$areaNames[0],'status'=>2,'order'=>7,'intro'=>$area['intro'],'is_delete'=>0]);$out['source_created_times']['area']=createdTimeEvidence($areaCreated);$save('area_create',$j);
   $before=$ownCounts();$j=$call('area_duplicate_rejected','areaCreate',$area,1);if($ownCounts()!==$before)throw new RuntimeException('Duplicate area inserted');$out['routes']['area_duplicate_rejected']['no_extra_rows']=true;
   $j=$call('area_missing_name_rejected','areaCreate',['status'=>2],1);if($ownCounts()!==$before)throw new RuntimeException('Invalid area inserted');$out['routes']['area_missing_name_rejected']['no_extra_rows']=true;
   $old=$setOldTime('area','2020-01-01 00:00:00');$area2=array_merge($area,['id'=>$areaId,'name'=>$areaNames[1],'order'=>19,'intro'=>'CDX修改后简介']);$j=$call('area_update','areaUpdate',$area2);$areaUpdated=$readOwn('area',['name'=>$areaNames[1],'status'=>2,'order'=>19,'intro'=>$area2['intro']]);$out['source_update_time_advanced']['area']=$areaUpdated['update_time']!==$old;$save('area_update',$j);$old=$setOldTime('area','2020-01-02 00:00:00');$j=$call('area_update_noop','areaUpdate',$area2);$areaUpdated=$readOwn('area',['name'=>$areaNames[1],'status'=>2,'order'=>19]);$out['source_noop_time_preserved']['area']=$areaUpdated['update_time']===$old;$save('area_update_noop',$j);$j=$call('area_readback','areaDetail',['id'=>$areaId]);$save('area_readback',$j);
   $shop=['name'=>$shopNames[0],'area_id'=>$areaId,'location'=>"CDX '测试位置'",'status'=>2,'admin_name'=>'CDX测试','admin_mobile'=>'','intro'=>'CDX停用店','file_path'=>''];$j=$call('disabled_parent_rejected','restaurantCreate',$shop,1);if($ownCounts()['shops']!==0)throw new RuntimeException('Disabled-parent shop inserted');$out['routes']['disabled_parent_rejected']['no_extra_rows']=true;
   $j=$call('area_enable_own','areaStatus',['id'=>$areaId,'status'=>1]);$readOwn('area',['status'=>1]);$save('area_enable_own',$j);
   $j=$call('restaurant_create','restaurantCreate',$shop);$identity=r($db,'SELECT ID FROM '.$p.'restaurant_shop WHERE NAME=? AND CREATE_BY=? AND AREA_ID=?',[$shopNames[0],$marker,$areaId]);if(count($identity)!==1)throw new RuntimeException('Created shop identity');$shopId=(int)$identity[0]['id'];if(array_sum($refs())!==0)throw new RuntimeException('Own external references');$shopCreated=$readOwn('shop',['name'=>$shopNames[0],'area_id'=>$areaId,'status'=>2,'location'=>$shop['location'],'is_delete'=>0]);$out['source_created_times']['shop']=createdTimeEvidence($shopCreated);$save('restaurant_create',$j);
   $j=$call('area_disable_own','areaStatus',['id'=>$areaId,'status'=>2]);$readOwn('area',['status'=>2]);$save('area_disable_own',$j);
   $before=$ownCounts();$j=$call('restaurant_disabled_parent_repeat_rejected','restaurantCreate',$shop,1);if($ownCounts()!==$before)throw new RuntimeException('Disabled-parent shop inserted');$out['routes']['restaurant_disabled_parent_repeat_rejected']['no_extra_rows']=true;
   $old=$setOldTime('shop','2020-01-01 00:00:00');$shop2=array_merge($shop,['id'=>$shopId,'name'=>$shopNames[1],'location'=>'CDX更新位置','intro'=>'CDX更新停用店']);$j=$call('restaurant_update','restaurantUpdate',$shop2);$shopUpdated=$readOwn('shop',['name'=>$shopNames[1],'area_id'=>$areaId,'status'=>2,'location'=>$shop2['location'],'intro'=>$shop2['intro']]);$out['source_update_time_advanced']['shop']=$shopUpdated['update_time']!==$old;$save('restaurant_update',$j);$old=$setOldTime('shop','2020-01-02 00:00:00');$j=$call('restaurant_update_noop','restaurantUpdate',$shop2);$shopUpdated=$readOwn('shop',['name'=>$shopNames[1],'status'=>2]);$out['source_noop_time_preserved']['shop']=$shopUpdated['update_time']===$old;$save('restaurant_update_noop',$j);$j=$call('restaurant_readback','restaurantDetail',['id'=>$shopId]);$save('restaurant_readback',$j);
   $j=$call('restaurant_move_parent_rejected','restaurantUpdate',array_merge($shop2,['area_id'=>$areaId+1000000]),1);$readOwn('shop',['area_id'=>$areaId]);$out['routes']['restaurant_move_parent_rejected']['own_original_parent_preserved']=true;
   foreach([['area','areaStatus','area',&$areaId],['restaurant','restaurantStatus','shop',&$shopId]] as $spec){foreach([1,2,2] as $i=>$status){$old=$setOldTime($spec[2],'2020-01-0'.($i+3).' 00:00:00');$name=$spec[0].'_status_'.$i;$j=$call($name,$spec[1],['id'=>$spec[3],'status'=>$status]);$state=$readOwn($spec[2],['status'=>$status]);$out['source_status_timestamp_semantics'][$name]=$i===2?$state['update_time']===$old:$state['update_time']!==$old;$save($name,$j);if($spec[0]==='area'&&$i===0){$before=$ownCounts();$j=$call('restaurant_duplicate_rejected','restaurantCreate',$shop2,1);if($ownCounts()!==$before)throw new RuntimeException('Duplicate shop inserted');$out['routes']['restaurant_duplicate_rejected']['no_extra_rows']=true;}}$name=$spec[0].'_invalid_status_rejected';$j=$call($name,$spec[1],['id'=>$spec[3],'status'=>3],1);$readOwn($spec[2],['status'=>2]);$out['routes'][$name]['own_status_preserved']=true;}
   $out['final_own_states']=['area_status'=>2,'shop_status'=>2,'external_reference_counts'=>$refs()];
  }catch(Throwable $e){$executionFailure=['stage'=>$stage,'class'=>get_class($e),'message_sha12'=>substr(hash('sha256',$e->getMessage()),0,12)];}
  finally{
   $stage='cleanup';try{
    foreach([['restaurant_area',$areaNames,'area'],['restaurant_shop',$shopNames,'shop']] as $spec){$rows=r($db,'SELECT ID,NAME,CREATE_BY FROM '.$p.$spec[0].' WHERE NAME IN (?,?)',$spec[1]);if(count($rows)>1)throw new RuntimeException('Cleanup ambiguous marker');if($rows){if($rows[0]['create_by']!==$marker)throw new RuntimeException('Cleanup ownership');if($spec[2]==='area')$areaId=(int)$rows[0]['id'];else $shopId=(int)$rows[0]['id'];}}
    if(array_sum($refs())!==0)throw new RuntimeException('Cleanup blocked by external reference');
    foreach([['restaurant_shop',$shopNames,'shop'],['restaurant_area',$areaNames,'area']] as $spec){$rows=r($db,'SELECT ID,NAME,CREATE_BY FROM '.$p.$spec[0].' WHERE NAME IN (?,?)',$spec[1]);if(count($rows)>1)throw new RuntimeException('Cleanup ambiguous marker');foreach($rows as $row){if($row['create_by']!==$marker)throw new RuntimeException('Cleanup ownership');$args=[(int)$row['id'],$row['name'],$marker];$sql='DELETE FROM '.$p.$spec[0].' WHERE ID=? AND NAME=? AND CREATE_BY=?';if($spec[2]==='shop'){$sql.=' AND AREA_ID=?';$args[]=$areaId;}$q=$db->prepare($sql);$q->execute($args);if($q->rowCount()!==1)throw new RuntimeException('Cleanup row count');$cleanupDeletes++;}}
    $out['own_after']=$ownCounts();if($out['own_after']!==['areas'=>0,'shops'=>0])throw new RuntimeException('Own residual rows');$out['cleanup_zero']=true;
   }catch(Throwable $e){$out['cleanup_failure']=['class'=>get_class($e),'message_sha12'=>substr(hash('sha256',$e->getMessage()),0,12)];}
  }
  $out['cleanup_delete_rows']=$cleanupDeletes;$out['non_target_counts_after']=$counts();$after=['areas'=>r($db,'SELECT * FROM '.$p.'restaurant_area ORDER BY ID'),'shops'=>r($db,'SELECT * FROM '.$p.'restaurant_shop ORDER BY ID'),'stalls'=>r($db,'SELECT * FROM '.$p.'restaurants ORDER BY ID')];$out['natural_after']=h($after);$out['natural_unchanged']=$out['natural_before']===$out['natural_after'];$out['execution_failure']=$executionFailure;$out['database_writes']='Only own area/shop HTTP writes, explicit old-update_time fixture updates, and precise cleanup deletes';
  $creationPass=true;foreach($out['source_created_times']??[] as $evidence)$creationPass=$creationPass&&$evidence['same_raw_value']&&$evidence['create_no_nonzero_fraction']&&$evidence['update_no_nonzero_fraction'];
  $out['compatibility_pass']=$creationPass&&$executionFailure===''&&empty($out['timestamp_wire_differences'])&&!in_array(false,$out['source_update_time_advanced']??[],true)&&!in_array(false,$out['source_noop_time_preserved']??[],true)&&!in_array(false,$out['source_status_timestamp_semantics']??[],true);
  echo json_encode($out,JSON_UNESCAPED_UNICODE|JSON_INVALID_UTF8_SUBSTITUTE),"\n";exit($executionFailure===''&&!isset($out['cleanup_failure'])?0:2);
 }
 $out['database_writes']=0;$out['failure']='';
}catch(Throwable $e){$out['failure']=['stage'=>$stage,'class'=>get_class($e),'message_sha12'=>substr(hash('sha256',$e->getMessage()),0,12)];}
echo json_encode($out,JSON_UNESCAPED_UNICODE|JSON_INVALID_UTF8_SUBSTITUTE),"\n";exit(empty($out['failure'])?0:2);
