<?php
declare(strict_types=1);
date_default_timezone_set('Asia/Shanghai');define('CDX_MS_CONTEXT',true);require __DIR__.'/preflight.php';$ref=json_decode((string)file_get_contents(__DIR__.'/private-reference.json'),true);$marker='CDXMS10'.$lane.strtoupper(bin2hex(random_bytes(4)));$ownId=0;$out=['lane'=>$lane,'marker'=>$marker,'cases'=>[],'source_checks'=>[],'failure'=>null];$stage='boundary';$enabled=false;
$hash=static function($v):string{return hash('sha256',serialize($v));};$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];}return is_array($v)?['array'=>array_map($shape,$v)]:(is_int($v)||is_float($v)?'number':gettype($v));};
$admin=$q('SELECT ID,UUID FROM '.$p.'user WHERE ID=(SELECT MIN(ID) FROM '.$p.'user WHERE TYPE=0 AND IS_DELETE=0)')[0];require_once $root.'/application/base/Security.php';$token=\app\base\Security::encrypt(implode("\t",[(string)$admin['id'],(string)$admin['uuid'],'CDXMS10ADMIN','0','','','CDXMS10ADMIN']));$bodies=[];
$rq=static function(string $label,string $action,array $params)use($lane,$token,&$out,&$bodies,$shape):array{$path='/api/marketingStrategy/'.$action;$raw=@file_get_contents('http://127.0.0.1:'.($lane==='B'?18301:18302).$path,false,stream_context_create(['http'=>['method'=>'POST','header'=>'token: '.$token."\r\nContent-Type: application/json\r\n",'content'=>json_encode($params,JSON_UNESCAPED_UNICODE),'ignore_errors'=>true,'timeout'=>30]]));preg_match('#^HTTP/\S+\s+(\d+)#',$http_response_header[0]??'',$m);$body=json_decode((string)$raw,true);$wire=json_decode((string)$raw);$bodies[$label]=$wire;$out['cases'][]=['scenario'=>$label,'method'=>'POST','path'=>$path,'http_status'=>(int)($m[1]??0),'code'=>$body['code']??null,'wire_shape'=>$shape($wire),'raw_wire_sha256'=>hash('sha256',(string)$raw)];return [$body,count($out['cases'])-1];};
$own=static function()use(&$ownId,$q,$p,$marker):array{$r=$q('SELECT * FROM '.$p.'marketing_strategy WHERE ID=? AND STRATEGY_NAME=?',[$ownId,$marker]);if(count($r)!==1)throw new RuntimeException('Owned main source missing');return $r[0];};
$relations=static function()use(&$ownId,$q,$p,$tables):array{$r=[];foreach(array_slice($tables,1) as $t)$r[$t]=$q('SELECT * FROM '.$p.$t.' WHERE STRATEGY_ID=? ORDER BY ID',[$ownId]);return $r;};
$verifyDetail=static function(string $label)use($rq,&$ownId,$own,$relations,$check,&$out,$ref):void{[$body,$index]=$rq($label,'detail',['id'=>$ownId]);$check(($body['code']??null)===0,'Owned detail positive');$raw=$own();$d=$body['data'];$mismatch=[];foreach($raw as $key=>$value){$actual=$d[$key]??null;$same=$actual===$value;if(!$same&&is_numeric($actual)&&is_numeric($value))$same=bccomp((string)$actual,(string)$value,8)===0;if(!$same&&in_array($key,['create_time','update_time'],true)&&is_string($value)&&preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{1,6}$/D',$value))$same=$actual===substr($value,0,19);if(!$same)$mismatch[]=$key;}$r=$relations();$check($mismatch===[],'Own all original field values');$check($d['department_uuids']===[$ref['department']['uuid']]&&$d['department_names']===[$ref['department']['name']]&&$d['identity_ids']===[(int)$ref['identity']['id']]&&$d['identity_names']===[$ref['identity']['identity_type']]&&$d['restaurant_ids']===[(int)$ref['stall']['id']]&&$d['restaurant_names']===[$ref['stall']['name']],'Owned relations and names');$check($d['application_scenarios']===[1]&&$d['meal_times']===[1]&&$d['effective_range']===['2010-01-01','2010-01-02'],'Owned scenario/meal/effective');$ranges=[];foreach($r['marketing_strategy_time_range'] as $row)$ranges[]=['id'=>(int)$row['id'],'start_time'=>substr($row['start_time'],0,5),'end_time'=>substr($row['end_time'],0,5),'strategy_type'=>$row['strategy_type']===null?null:(int)$row['strategy_type'],'strategy_value'=>$row['strategy_value']===null?null:(float)$row['strategy_value']];$check($d['time_ranges']===$ranges,'All own time-range source values/types/order');$out['cases'][$index]['all_original_fields_and_six_relations_source_equal']=true;$out['cases'][$index]['raw_column_count']=count($raw);$out['cases'][$index]['strategy_value_type']=gettype($d['strategy_value']);$out['cases'][$index]['create_time_length']=strlen($d['create_time']);$out['cases'][$index]['update_time_length']=strlen($d['update_time']);};
$params=['strategyName'=>$marker,'priority'=>7,'strategyType'=>2,'strategyValue'=>'0.01','applicationScenarios'=>[1],'timeRangeMode'=>1,'time_ranges'=>[['start_time'=>'08:00','end_time'=>'09:00']],'effectiveType'=>2,'effectiveRange'=>['2010-01-01','2010-01-02'],'departmentUuids'=>[$ref['department']['uuid']],'identityIds'=>[(int)$ref['identity']['id']],'restaurantIds'=>[(int)$ref['stall']['id']],'meal_times'=>[1],'isFirstConsumeOnly'=>0,'status'=>0,'remark'=>'CDX营销中文"备注\\'];
try{$boundary=json_decode((string)file_get_contents('http://127.0.0.1:'.($lane==='B'?18301:18302).'/__poc_boundary'),true);$check($boundary['allowlist']==='owned-expired-marketing-only'&&$boundary['external_stubs']&&$boundary['original_debug']===false,'Private boundary');
 $stage='create';[$body,$i]=$rq('create','create',$params);$check(($body['code']??null)===0,'Create success');$rows=$q('SELECT ID FROM '.$p.'marketing_strategy WHERE STRATEGY_NAME=? AND CREATE_BY=?',[$marker,'CDXMS10ADMIN']);$check(count($rows)===1,'Owned created main row');$ownId=(int)$rows[0]['id'];file_put_contents(__DIR__.'/private-own.json',json_encode(['marker'=>$marker,'strategy_id'=>$ownId]));chmod(__DIR__.'/private-own.json',0600);$raw=$own();$check((int)$raw['status']===0&&substr($raw['effective_end'],0,10)==='2010-01-02'&&bccomp((string)$raw['strategy_value'],'0.01',2)===0,'Owned expired disabled initial source');$r=$relations();foreach($r as $rows)$check(count($rows)===1,'One row in each relation');$out['cases'][$i]['main_and_six_relation_writes_read_back']=true;$verifyDetail('detail_created');
 $stage='duplicate';$beforeDup=[$own(),$relations()];[$body,$i]=$rq('duplicate_name','create',$params);$check(($body['code']??null)===1&&[$own(),$relations()]===$beforeDup,'Duplicate rejected no own mutation');$out['cases'][$i]['owned_source_unchanged']=true;
 foreach(['lists','select'] as $action){$stage=$action;[$body,$i]=$rq($action,$action,$action==='lists'?['strategyName'=>$marker,'page'=>1,'pageSize'=>5]:[]);$check(($body['code']??null)===0,'Read positive '.$action);$rows=$action==='lists'?$body['data']['data']:$body['data'];$matches=array_values(array_filter($rows,static function($r)use($ownId){return (int)$r['id']===$ownId;}));$check(count($matches)===1&&$matches[0]['strategy_name']===$marker,'Own nonempty read source '.$action);$out['cases'][$i]['owned_nonempty_source_equal']=true;}
 $stage='update-changed';$db->prepare('UPDATE '.$p.'marketing_strategy SET UPDATE_TIME=? WHERE ID=? AND STRATEGY_NAME=?')->execute(['2001-01-01 00:00:00',$ownId,$marker]);$params['id']=$ownId;$params['strategyValue']='0.02';$params['priority']=8;$params['remark']='CDX更新中文';[$body,$i]=$rq('update_changed','update',$params);$check(($body['code']??null)===0,'Real update changed');$raw=$own();$check(bccomp((string)$raw['strategy_value'],'0.02',2)===0&&(int)$raw['priority']===8&&$raw['remark']===$params['remark'],'Changed fields source');$out['source_checks']['changed_update_time_advanced']=substr($raw['update_time'],0,19)!=='2001-01-01 00:00:00';$out['cases'][$i]['changed_fields_source_equal']=true;$verifyDetail('detail_updated');
 $stage='update-noop';$db->prepare('UPDATE '.$p.'marketing_strategy SET UPDATE_TIME=? WHERE ID=? AND STRATEGY_NAME=?')->execute(['2002-01-01 00:00:00',$ownId,$marker]);[$body,$i]=$rq('update_noop','update',$params);$check(($body['code']??null)===0,'Real no-op update');$out['source_checks']['noop_update_time_not_advanced']=substr($own()['update_time'],0,19)==='2002-01-01 00:00:00';$verifyDetail('detail_noop');
 $stage='status';[$body,$i]=$rq('status_enable_expired','changeStatus',['id'=>$ownId,'status'=>1]);$enabled=true;$check(($body['code']??null)===0&&(int)$own()['status']===1,'Enable only expired owned');$out['cases'][$i]['owned_expired_status_verified']=true;[$body,$i]=$rq('status_disable','changeStatus',['id'=>$ownId,'status'=>0]);$check(($body['code']??null)===0&&(int)$own()['status']===0,'Restore disabled');$enabled=false;$out['cases'][$i]['owned_status_verified']=true;
 $stage='delete';[$body,$i]=$rq('delete','delete',['id'=>$ownId]);$check(($body['code']??null)===0&&(int)$own()['is_delete']===1,'Actual soft delete');$remaining=$relations();foreach($remaining as $t=>$rows)$check(count($rows)===($t==='marketing_strategy_mealtimes'?1:0),'Original relation-delete semantics '.$t);$out['source_checks']['delete_mealtimes_retained_as_original']=count($remaining['marketing_strategy_mealtimes'])===1;$out['cases'][$i]['soft_delete_and_five_relations_removed_verified']=true;[$body,$i]=$rq('detail_deleted','detail',['id'=>$ownId]);$check(($body['code']??null)===1,'Deleted detail rejected');[$body,$i]=$rq('missing_detail','detail',['id'=>2147000800]);$check(($body['code']??null)===1,'Absent positive ID rejected');
}catch(Throwable $e){$out['failure']=['stage'=>$stage,'class'=>get_class($e),'message'=>$e->getMessage()];}
finally{if($enabled&&$ownId>0){$rq('safety_disable_after_failure','changeStatus',['id'=>$ownId,'status'=>0]);$enabled=false;}$out['retained']=['strategy_id'=>$ownId,'marker'=>$marker,'status'=>$ownId?(int)$own()['status']:null,'is_delete'=>$ownId?(int)$own()['is_delete']:null,'relation_counts'=>$ownId?array_map('count',$relations()):[]];$out['natural_seven_tables_unchanged']=true;foreach($tables as $t){$sql='SELECT * FROM '.$p.$t.($t==='marketing_strategy'?' WHERE ID<>?':' WHERE STRATEGY_ID<>?').' ORDER BY ID';$rows=$q($sql,[$ownId]);$out['natural_seven_tables_unchanged']=$out['natural_seven_tables_unchanged']&&$hash($rows)===$report['tables'][$t]['sha256'];}$out['reference_rows_unchanged']=$q('SELECT UUID,NAME FROM '.$p.'department WHERE UUID=?',[$ref['department']['uuid']])===[$ref['department']]&&$q('SELECT ID,IDENTITY_TYPE FROM '.$p.'identity_type WHERE ID=?',[(int)$ref['identity']['id']])===[$ref['identity']];$out['owned_stall_unchanged']=$q('SELECT ID,UUID,NAME,CREATE_BY,STALL_TYPE FROM '.$p.'restaurants WHERE ID=?',[(int)$ref['stall']['id']])===[$ref['stall']];file_put_contents(__DIR__.'/private-bodies.json',json_encode($bodies,JSON_UNESCAPED_UNICODE));chmod(__DIR__.'/private-bodies.json',0600);file_put_contents(__DIR__.'/crud-private.json',json_encode($out,JSON_PRETTY_PRINT));chmod(__DIR__.'/crud-private.json',0600);}
echo json_encode($out),"\n";exit($out['failure']?2:0);
