<?php
declare(strict_types=1);
define('RFJ_CONTEXT_ONLY',true);require __DIR__.'/preflight.php';
$report=['lane'=>$lane,'preflight_reused'=>true,'fixture_kind'=>'completed-order-state-only-not-a-real-payment','cases'=>[],'database_writes'=>'own four-table fixture and real apply/reject/audit only'];$stage='boundary';$own=['marker'=>'CDXRFJ10'.$lane.strtoupper(bin2hex(random_bytes(4))),'order_id'=>0,'stall_id'=>0,'refund_ids'=>[]];$before=null;$actor=null;
$check=static function(bool $ok,string $reason):void{if(!$ok)throw new RuntimeException($reason);};
$hash=static function($v):string{return hash('sha256',json_encode($v,JSON_UNESCAPED_UNICODE|JSON_INVALID_UTF8_SUBSTITUTE|JSON_PRESERVE_ZERO_FRACTION));};
$sorted=static function(array $rows):array{foreach($rows as &$row)ksort($row);unset($row);usort($rows,static function($x,$y){return strcmp(serialize($x),serialize($y));});return $rows;};
$snapshot=static function()use($store,$ai,$q,$s,$a,$chosen,$sorted,$own):array{
 $r=['store_user'=>$q($store,'SELECT * FROM '.$s.'user WHERE ID=?',[(int)$chosen['store_user']['id']]),'store_staff'=>$q($store,'SELECT * FROM '.$s.'staff WHERE UUID=?',[$chosen['staff']['uuid']]),'store_existing_orders'=>$q($store,'SELECT * FROM '.$s.'meal_order WHERE USER_ID=? AND ORDER_NO<>?',[(int)$chosen['store_user']['id'],$own['marker']])];
 foreach(['user','user_balance_log','user_subsidy','user_subsidy_log','user_subsidy_used','user_gift_log','account_settlement'] as $table)$r['ai_'.$table]=$q($ai,'SELECT * FROM '.$a.$table.' WHERE USER_ID=?',[(int)$chosen['ai_user']['user_id']]);
 foreach($r as &$rows)$rows=$sorted($rows);unset($rows);return $r;};
$shape=static function($v)use(&$shape){if(is_object($v)){$r=[];foreach(get_object_vars($v) as $k=>$x)$r[$k]=$shape($x);ksort($r);return ['object'=>$r];}if(is_array($v))return ['array'=>array_map($shape,$v)];return is_int($v)||is_float($v)?'number':gettype($v);};
$journal=static function()use(&$own):void{file_put_contents(__DIR__.'/private-write-journal.json',json_encode($own));chmod(__DIR__.'/private-write-journal.json',0600);};
$insert=static function(string $table,array $data)use($store,$out,$s,$check):void{$allowed=array_map(static function($c){return strtolower($c['column_name']);},$out['schema'][$table]);foreach(array_keys($data) as $key)$check(in_array($key,$allowed,true),'Fixture schema column');$sql='INSERT INTO '.$s.$table.' ('.implode(',',array_keys($data)).') VALUES ('.implode(',',array_fill(0,count($data),'?')).')';$store->prepare($sql)->execute(array_values($data));};
$clearOwnCache=static function(string $key):void{if(!preg_match('/^meal_order_refund_(audit|apply)_[0-9]+$/D',$key))throw new RuntimeException('Cache key');$file=__DIR__.'/cache/RFJ10_/'.md5($key).'.php';if(!is_file($file)||is_link($file)||!unlink($file))throw new RuntimeException('Exact own File cache key not removed');};
$request=static function(string $name,string $path,array $params,?string $fixedNo=null)use($lane,$root,&$actor,$shape,$hash,&$own,$check):array{
 require_once $root.'/application/base/Security.php';$token=\app\base\Security::encrypt(implode("\t",[(string)$actor['id'],(string)$actor['uuid'],'CDXRFJ10AUDIT','0','','','CDXRFJ10AUDIT']));$headers='token: '.$token."\r\nContent-Type: application/x-www-form-urlencoded\r\n";
 if($fixedNo!==null){$nonce=bin2hex(random_bytes(16));file_put_contents(__DIR__.'/private-request-order-no.json',json_encode(['nonce'=>$nonce,'order_no'=>$fixedNo]));chmod(__DIR__.'/private-request-order-no.json',0600);$headers.='X-RFJ-Fixture-Nonce: '.$nonce."\r\n";}
 $raw=@file_get_contents('http://127.0.0.1:'.($lane==='B'?18301:18302).$path,false,stream_context_create(['http'=>['method'=>'POST','header'=>$headers,'content'=>http_build_query($params),'ignore_errors'=>true,'timeout'=>20]]));preg_match('#^HTTP/\S+\s+(\d+)#',$http_response_header[0]??'',$m);$body=json_decode((string)$raw,true);$wire=json_decode((string)$raw);$check(is_array($body)&&is_object($wire),'Real JSON response '.$name);
 $e=['scenario'=>$name,'method'=>'POST','path'=>$path,'http_status'=>(int)($m[1]??0),'code'=>$body['code']??null,'wire_shape'=>$shape($wire),'raw_response_sha256'=>hash('sha256',(string)$raw)];
 if(isset($wire->data->refund_id)){$id=$wire->data->refund_id;$check(is_int($id)&&in_array($id,$own['refund_ids'],true),'Owned returned numeric refund ID');$wire->data->refund_id=array_search($id,$own['refund_ids'],true)+1;$e['own_id_mapping_verified']=true;}
 $e['normalized_wire_sha256']=$hash($wire);return [$e,$body];
};
$state=static function()use($store,$q,$s,&$own):array{$audits=$q($store,'SELECT * FROM '.$s.'security_audit_log WHERE OPERATOR=? AND TARGET_TYPE=? AND TARGET_ID=? ORDER BY ID',['CDXRFJ10AUDIT','order',(string)$own['order_id']]);foreach($own['refund_ids'] as $id)$audits=array_merge($audits,$q($store,'SELECT * FROM '.$s.'security_audit_log WHERE OPERATOR=? AND TARGET_TYPE=? AND TARGET_ID=? ORDER BY ID',['CDXRFJ10AUDIT','refund',(string)$id]));usort($audits,static function($x,$y){return (int)$x['id']<=>(int)$y['id'];});return ['order'=>$q($store,'SELECT ID,ORDER_NO,USER_ID,RESTAURANT_ID,PAY_PRICE,REFUND_AMOUNT,REFUND_STATUS,ORDER_STATUS FROM '.$s.'meal_order WHERE ID=? AND ORDER_NO=?',[$own['order_id'],$own['marker']]),'refunds'=>$q($store,'SELECT * FROM '.$s.'meal_order_refund WHERE MEAL_ORDER_ID=? ORDER BY ID',[$own['order_id']]),'audits'=>$audits];};
try{
 $b=json_decode((string)file_get_contents('http://127.0.0.1:'.($lane==='B'?18301:18302).'/__poc_boundary'),true);$check(is_array($b)&&$b['private_file_cache']&&$b['redis_disabled']&&$b['external_stubs_configured']&&!$b['real_queue']&&$b['allowlist']==='own-refund-apply-reject-only','Private boundary');$report['boundary']=$b;
 foreach($out['triggers'] as $triggers)$check($triggers===[],'No hidden table triggers');$stage='wallet-before';$before=$snapshot();$report['wallet_source_before']=array_map(static function($rows)use($hash){return ['rows'=>count($rows),'sha'=>$hash($rows)];},$before);
 $stage='actor-read';$rows=$q($store,'SELECT ID,UUID,TYPE FROM '.$s.'user WHERE ID=(SELECT MIN(ID) FROM '.$s.'user WHERE TYPE=0 AND IS_DELETE=0)');$check(count($rows)===1,'Existing admin actor');$actor=$rows[0];
 $stage='own-stall-insert';$uuid=sprintf('%s-%s-%s-%s-%s',bin2hex(random_bytes(4)),bin2hex(random_bytes(2)),bin2hex(random_bytes(2)),bin2hex(random_bytes(2)),bin2hex(random_bytes(6)));$own['stall_uuid']=$uuid;$journal();
 $insert('restaurants',['uuid'=>$uuid,'name'=>$own['marker'],'create_by'=>$own['marker'],'book_rules'=>'{}','order_rules'=>'{}','status'=>0,'is_delete'=>1]);$rows=$q($store,'SELECT ID FROM '.$s.'restaurants WHERE UUID=? AND NAME=? AND CREATE_BY=?',[$uuid,$own['marker'],$own['marker']]);$check(count($rows)===1,'Own stall unique');$own['stall_id']=(int)$rows[0]['id'];$journal();
 $stage='own-order-insert';$insert('meal_order',['order_no'=>$own['marker'],'user_id'=>(int)$chosen['store_user']['id'],'restaurant_id'=>$own['stall_id'],'pay_price'=>'0.01','order_price'=>'0.01','total_price'=>'0.01','refund_amount'=>'0.00','refund_status'=>0,'order_status'=>30,'pay_status'=>20,'meal_date'=>date('Y-m-d'),'meal_times'=>1,'bind_time'=>date('Y-m-d H:i:s'),'is_delete'=>0]);$rows=$q($store,'SELECT ID FROM '.$s.'meal_order WHERE ORDER_NO=? AND USER_ID=?',[$own['marker'],(int)$chosen['store_user']['id']]);$check(count($rows)===1,'Own order unique');$own['order_id']=(int)$rows[0]['id'];$journal();
 // Capture generated IDs through source before response mapping, not by trusting an arbitrary response ID.
 $apply=static function(string $label,int $round)use(&$own,$store,$q,$s,$request,$check,&$report,$journal,$hash):void{
  // A fresh committed source query after HTTP owns any generated ID before canonical mapping.
  $old=$own['refund_ids'];$nonce=bin2hex(random_bytes(16));$number=$own['marker'].$round;file_put_contents(__DIR__.'/private-request-order-no.json',json_encode(['nonce'=>$nonce,'order_no'=>$number]));chmod(__DIR__.'/private-request-order-no.json',0600);
  global $lane,$root,$actor,$shape;
  require_once $root.'/application/base/Security.php';$token=\app\base\Security::encrypt(implode("\t",[(string)$actor['id'],(string)$actor['uuid'],'CDXRFJ10AUDIT','0','','','CDXRFJ10AUDIT']));$raw=@file_get_contents('http://127.0.0.1:'.($lane==='B'?18301:18302).'/p/mealOrder/refund',false,stream_context_create(['http'=>['method'=>'POST','header'=>'token: '.$token."\r\nContent-Type: application/x-www-form-urlencoded\r\nX-RFJ-Fixture-Nonce: ".$nonce."\r\n",'content'=>http_build_query(['order_id'=>$own['order_id'],'refund_amount'=>'0.01','apply_desc'=>$own['marker']]),'ignore_errors'=>true,'timeout'=>20]]));preg_match('#^HTTP/\S+\s+(\d+)#',$http_response_header[0]??'',$m);$body=json_decode((string)$raw,true);$wire=json_decode((string)$raw);$check(is_array($body)&&($body['code']??-1)===0,'Apply success '.$label);
  $rows=$q($store,'SELECT * FROM '.$s.'meal_order_refund WHERE MEAL_ORDER_ID=? AND ORDER_NO=? AND APPLY_DESC=?',[$own['order_id'],'RF'.$number,$own['marker']]);$check(count($rows)===1,'Owned source refund');$id=(int)$rows[0]['id'];$actualId=$body['data']['refund_id'];$report['apply_id_observation']=['response_type'=>gettype($actualId),'source_type'=>gettype($rows[0]['id']),'id_decimal_text_equal'=>(string)$actualId===(string)$rows[0]['id']];$check(!in_array($id,$old,true)&&(is_int($actualId)||is_string($actualId))&&(string)$actualId===(string)$id,'Generated refund ID owned');$own['refund_ids'][]=$id;$journal();$check((int)$rows[0]['refund_status']===1&&bccomp((string)$rows[0]['refund_amount'],'0.01',2)===0,'Applied source status and amount');$wire->data->refund_id=is_string($actualId)?(string)$round:$round;$report['cases'][]=['scenario'=>$label,'method'=>'POST','path'=>'/p/mealOrder/refund','http_status'=>(int)($m[1]??0),'code'=>$body['code'],'wire_shape'=>$shape(json_decode((string)$raw)),'normalized_wire_sha256'=>$hash($wire),'own_id_mapping_verified'=>true,'refund_source_pending'=>true];
 };
 $stage='apply-first';$apply('apply_first',1);$initial=$state();$check((int)$initial['order'][0]['refund_status']===1,'Order pending');
 $stage='reject';[$e,$body]=$request('reject_success','/p/mealOrder/refundReject',['refund_id'=>$own['refund_ids'][0]]);$check($e['http_status']===200&&$body['code']===0,'Reject successful');$afterReject=$state();$r=$afterReject['refunds'][0];$check((int)$r['refund_status']===3&&(int)$afterReject['order'][0]['refund_status']===0&&$r['audit_user']==='CDXRFJ10AUDIT'&&!empty($r['audit_time']),'Reject source status/auditor/time');$e['source_rejected_refund_and_reset_order']=true;$report['cases'][]=$e;
 $stage='repeat-cache';[$e,$body]=$request('reject_immediate_cache','/p/mealOrder/refundReject',['refund_id'=>$own['refund_ids'][0]]);$check($body['code']===1&&$body['message']==='退款审核正在处理，请勿重复提交','Real private cache rejection');$check($state()===$afterReject,'Cache rejection no source change');$e['all_owned_source_unchanged']=true;$report['cases'][]=$e;
 $stage='repeat-status';$clearOwnCache('meal_order_refund_audit_'.$own['refund_ids'][0]);[$e,$body]=$request('reject_after_own_cache_clear','/p/mealOrder/refundReject',['refund_id'=>$own['refund_ids'][0]]);$check($body['code']==='2'&&$body['message']==='当前退款状态不可驳回','Real business state rejection');$check($state()===$afterReject,'State rejection no source change');$e['all_owned_source_unchanged']=true;$report['cases'][]=$e;
 $stage='apply-again';$clearOwnCache('meal_order_refund_apply_'.$own['order_id']);$apply('apply_again',2);$again=$state();$check(count($again['refunds'])===2&&(int)$again['refunds'][0]['refund_status']===3&&(int)$again['refunds'][1]['refund_status']===1&&(int)$again['order'][0]['refund_status']===1,'Reapply source states');
 $stage='missing-refund';[$e,$body]=$request('reject_missing','/p/mealOrder/refundReject',['refund_id'=>0]);$check($body['code']==='2'&&$body['message']==='退款订单不存在','Missing own ID rejection');$check($state()===$again,'Missing ID no source change');$e['all_owned_source_unchanged']=true;$report['cases'][]=$e;
 $stage='audit-source';$audit=$again['audits'];$events=array_count_values(array_column($audit,'event_code'));$check(count($audit)===3&&($events['meal_order_refund_apply']??0)===2&&($events['meal_order_refund_reject']??0)===1,'Audit events exactly three');
 foreach($audit as $row){$applyEvent=$row['event_code']==='meal_order_refund_apply';$check($row['operator']==='CDXRFJ10AUDIT'&&$row['user_uuid']===$actor['uuid']&&$row['result']==='success'&&$row['request_method']==='POST'&&$row['module']==='meal_order'&&(int)$row['store_id']===10001&&(int)$row['is_delete']===0&&$row['terminal_ip']==='127.0.0.1','Audit identity fields');
  $check($row['event_name']===($applyEvent?'订单申请退款':'退款驳回')&&$row['action']===($applyEvent?'refund':'refundReject')&&$row['message']===($applyEvent?'提交退款申请成功':'驳回退款申请成功')&&$row['target_type']===($applyEvent?'order':'refund'),'Audit action labels');
  $check((string)$row['target_id']===($applyEvent?(string)$own['order_id']:(string)$own['refund_ids'][0]),'Audit owned target');$context=json_decode((string)$row['context_json'],true);
  if($applyEvent)$check(is_array($context)&&array_keys($context)===['refund_id','refund_amount']&&is_string($context['refund_id'])&&in_array((int)$context['refund_id'],$own['refund_ids'],true)&&$context['refund_amount']==='0.01','Audit exact apply context');else $check($context===[],'Audit reject empty context');
  $check(substr((string)$row['request_path'],-strlen($applyEvent?'/p/mealOrder/refund':'/p/mealOrder/refundReject'))===($applyEvent?'/p/mealOrder/refund':'/p/mealOrder/refundReject'),'Audit request path');
  $check((string)$row['create_time']===(string)$row['update_time']&&preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.0{1,6})?$/D',(string)$row['create_time'])===1,'Audit explicit second time fields');$check($row['trace_id']===''||preg_match('/^[0-9a-f]{13}$/D',(string)$row['trace_id'])===1,'Audit legacy trace shape');
 }
 $report['audit_source']=['rows'=>3,'apply_events'=>2,'reject_events'=>1,'identity_action_target_context_path_time_trace_defaults_verified'=>true];
 $report['failure']=null;
}catch(Throwable $e){$report['failure']=['stage'=>$stage,'class'=>get_class($e),'message_sha256'=>hash('sha256',$e->getMessage())];file_put_contents(__DIR__.'/private-error.json',json_encode(['stage'=>$stage,'message'=>$e->getMessage()]));chmod(__DIR__.'/private-error.json',0600);}
finally{
 $cleanup=['own_refunds_deleted'=>0,'own_audits_deleted'=>0,'own_orders_deleted'=>0,'own_stalls_deleted'=>0];
 try{if($own['order_id']>0){$refunds=$q($store,'SELECT ID FROM '.$s.'meal_order_refund WHERE MEAL_ORDER_ID=? AND APPLY_DESC=?',[$own['order_id'],$own['marker']]);$ids=array_map(static function($r){return (int)$r['id'];},$refunds);$check(count($q($store,'SELECT ID FROM '.$s.'initial_menu WHERE MEAL_ORDER_ID=?',[$own['order_id']]))===0,'Unexpected own order goods reference');$auditIds=[];foreach($ids as $id)foreach($q($store,'SELECT ID FROM '.$s.'security_audit_log WHERE OPERATOR=? AND TARGET_TYPE=? AND TARGET_ID=? AND EVENT_CODE=?',['CDXRFJ10AUDIT','refund',(string)$id,'meal_order_refund_reject']) as $v)$auditIds[]=(int)$v['id'];foreach($q($store,'SELECT ID FROM '.$s.'security_audit_log WHERE OPERATOR=? AND TARGET_TYPE=? AND TARGET_ID=? AND EVENT_CODE=?',['CDXRFJ10AUDIT','order',(string)$own['order_id'],'meal_order_refund_apply']) as $v)$auditIds[]=(int)$v['id'];foreach(array_unique($auditIds) as $id){$st=$store->prepare('DELETE FROM '.$s.'security_audit_log WHERE ID=? AND OPERATOR=?');$st->execute([$id,'CDXRFJ10AUDIT']);$cleanup['own_audits_deleted']+=$st->rowCount();}$st=$store->prepare('DELETE FROM '.$s.'meal_order_refund WHERE MEAL_ORDER_ID=? AND APPLY_DESC=?');$st->execute([$own['order_id'],$own['marker']]);$cleanup['own_refunds_deleted']=$st->rowCount();$st=$store->prepare('DELETE FROM '.$s.'meal_order WHERE ID=? AND ORDER_NO=? AND USER_ID=?');$st->execute([$own['order_id'],$own['marker'],(int)$chosen['store_user']['id']]);$cleanup['own_orders_deleted']=$st->rowCount();}
  if($own['stall_id']>0){$check(count($q($store,'SELECT ID FROM '.$s.'meal_order WHERE RESTAURANT_ID=?',[$own['stall_id']]))===0,'Unexpected own stall order reference');$check(count($q($store,'SELECT ID FROM '.$s.'equipment WHERE RESTAURANT_ID=?',[$own['stall_id']]))===0,'Unexpected own stall device reference');$st=$store->prepare('DELETE FROM '.$s.'restaurants WHERE ID=? AND UUID=? AND NAME=? AND CREATE_BY=?');$st->execute([$own['stall_id'],$own['stall_uuid'],$own['marker'],$own['marker']]);$cleanup['own_stalls_deleted']=$st->rowCount();}
  $cleanup['remaining_own_orders']=count($q($store,'SELECT ID FROM '.$s.'meal_order WHERE ORDER_NO=?',[$own['marker']]));$cleanup['remaining_own_refunds']=count($q($store,'SELECT ID FROM '.$s.'meal_order_refund WHERE APPLY_DESC=?',[$own['marker']]));$cleanup['remaining_own_stalls']=count($q($store,'SELECT ID FROM '.$s.'restaurants WHERE NAME=? AND CREATE_BY=?',[$own['marker'],$own['marker']]));
  if($before!==null){$after=$snapshot();$cleanup['both_business_databases_wallet_sources_unchanged']=$before===$after;$report['wallet_source_after']=array_map(static function($rows)use($hash){return ['rows'=>count($rows),'sha'=>$hash($rows)];},$after);$check($before===$after,'Wallet or existing source changed');}
 }catch(Throwable $e){$cleanup['failure']=['class'=>get_class($e),'message_sha256'=>hash('sha256',$e->getMessage())];}
 $report['cleanup']=$cleanup;
}
file_put_contents(__DIR__.'/result-private.json',json_encode($report,JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));chmod(__DIR__.'/result-private.json',0600);echo json_encode($report,JSON_UNESCAPED_UNICODE|JSON_INVALID_UTF8_SUBSTITUTE),"\n";exit($report['failure']!==null||isset($cleanup['failure'])?2:0);
