<?php

declare(strict_types=1);

$lane=(string)($argv[1]??'');$mode=(string)($argv[2]??'');
$self=$lane==='B'?(string)hash_file('sha256',__FILE__):'';
if(count($argv)!==3||!in_array($lane,['B','C'],true)||!in_array($mode,['--preflight','--execute','--audit'],true)
    ||($mode==='--execute'&&((string)getenv('CDX_GIFT_READ')!=='ACCOUNT_GIFT_READ4_V1'
        ||($lane==='B'?!hash_equals($self,(string)getenv('CDX_GIFT_SHA')):(string)getenv('CDX_GIFT_SOURCE_CHECKED')!=='1')))){
    echo json_encode(['status'=>'default_deny','connected'=>false,'http_sent'=>false,'dml'=>false]),PHP_EOL;exit(78);
}
$stage='bootstrap';set_exception_handler(static function(Throwable$e)use(&$stage):void{echo json_encode(['status'=>'redacted_stop','stage'=>$stage,'class'=>get_class($e),'code'=>(string)$e->getCode(),'message_sha'=>hash('sha256',$e->getMessage())]),PHP_EOL;exit(2);});
if($lane==='B'){$state='/tmp/cdx-rpe-b-worker.state';if(!is_file($state)||is_link($state)||(fileperms($state)&0777)!==0600)throw new RuntimeException('B worker state');$s=parse_ini_file($state,false,INI_SCANNER_RAW);$root=(string)($s['root']??'');$pid=(int)($s['pid']??0);$port=(int)($s['port']??0);if(!preg_match('~^/tmp/cdx-rpe-b-runtime\.[A-Za-z0-9]+$~D',$root)||$pid<=0||$port!==18082)throw new RuntimeException('B worker state invalid');}else{$root='/tmp/zhct-dm-poc-20260928.fcpiiN/store';$pid=112175;$port=18080;}
$base='http://127.0.0.1:'.$port;
$runtimeReady=static function()use($lane,$root,$pid,$port,$base):bool{if($lane==='B'){$cmd=(string)shell_exec('/bin/ps -p '.$pid.' -o command=');$owner=trim((string)shell_exec('/usr/sbin/lsof -nP -t -iTCP:'.$port.' -sTCP:LISTEN 2>/dev/null'));$ok=$owner===(string)$pid&&strpos($cmd,$root.'/public')!==false;}else{$cmd=(string)@file_get_contents('/proc/'.$pid.'/cmdline');$children=trim((string)@file_get_contents('/proc/'.$pid.'/task/'.$pid.'/children'));$ok=strpos($cmd,$root.'/public')!==false&&$children==='';}$b=json_decode((string)@file_get_contents($base.'/__poc_boundary'),true);return$ok&&is_array($b)&&($b['external_stubs']??null)===true&&($b['redis_isolated']??null)===true&&($b['real_queue']??true)===false;};
if(!$runtimeReady())throw new RuntimeException('HTTP boundary');
$hashes=['application/p/controller/AccountSetting.php'=>'65cf66aaa8866ec072841bcd4453d873f787d23c5a4fdb9036be9c3a7566fb66','application/p/logic/AccountSetting.php'=>'174036b5817358b659e086d9d4136e1bfc5bd3e8c75930e8e286c7341d6583e2','application/p/logic/AccountSettingPolicy.php'=>'385e3fa6a025d24a6cf8e7285e0a7f85990637116930b9024471378b87fd75ca','application/common/service/account/GiftAccountPolicy.php'=>'331a2216780de370872a3af897b18b51aa592e982e6b14fc035c3fb1c9a9a5e1','application/common/service/BaseService.php'=>'0d96474aa40a94abbb291a7730507e1980acf8a5d4f5ab0b8320beb538a637e0','application/base/AdminController.php'=>'7565b36fa4dda2c21cc4fa3b37753dbffb7c3a464bb8c11ea0f17ca00bececa5','application/base/Security.php'=>'b82e5ed982e6084a2b2942c033d4767428fb6f1012ba628f609477a7563eb4c3'];foreach($hashes as$p=>$sha)if(!is_file($root.'/'.$p)||hash_file('sha256',$root.'/'.$p)!==$sha)throw new RuntimeException('Source drift');
$stage='database-boundary';if($lane==='B'){$cfg=require'/Users/wiscourper/Documents/workspace/zhct/zhctproject/store/application/config/local/database.php';$process=' '.(string)shell_exec('/bin/ps eww -p '.$pid.' -o command=').' ';foreach(['hostname'=>'POC_HOST','hostport'=>'POC_PORT','database'=>'POC_SCHEMA','username'=>'POC_USER','password'=>'POC_PASSWORD']as$k=>$e){$v=(string)($cfg[$k]??($k==='hostport'?'3306':''));if($v===''||strpos($process,' '.$e.'='.$v.' ')===false)throw new RuntimeException('B DB drift');}$mainCfg=$cfg;$aiCfg=$cfg['ai_zhct'];$mainDsn='mysql:host='.$cfg['hostname'].';port='.($cfg['hostport']??3306).';dbname='.$cfg['database'].';charset='.($cfg['charset']??'utf8');$aiDsn='mysql:host='.$aiCfg['hostname'].';port='.($aiCfg['hostport']??3306).';dbname='.$aiCfg['database'].';charset='.($cfg['charset']??'utf8');$rule='yoshop_recharge_gift_rule';$flow='yoshop_user_gift_log';$user='ydy_user';$staff='ydy_staff';}
else{$env=[];foreach(explode("\0",(string)file_get_contents('/proc/'.$pid.'/environ'))as$x){$p=explode('=',$x,2);if(count($p)===2)$env[$p[0]]=$p[1];}foreach(['POC_HOST'=>'127.0.0.1','POC_PORT'=>'5237','POC_SCHEMA'=>'ZHCT_APP','POC_AI_HOST'=>'127.0.0.1','POC_AI_PORT'=>'5238','POC_AI_SCHEMA'=>'AIZHCT_APP']as$k=>$v)if((string)($env[$k]??'')!==$v)throw new RuntimeException('C DB drift');$mainCfg=['username'=>$env['POC_USER'],'password'=>$env['POC_PASSWORD']];$aiCfg=['username'=>$env['POC_AI_USER'],'password'=>$env['POC_AI_PASSWORD']];$mainDsn='dm:host=127.0.0.1;port=5237;schema=ZHCT_APP';$aiDsn='dm:host=127.0.0.1;port=5238;schema=AIZHCT_APP';$rule='YOSHOP_RECHARGE_GIFT_RULE';$flow='YOSHOP_USER_GIFT_LOG';$user='YDY_USER';$staff='YDY_STAFF';}
$mainConnect=static function()use($mainDsn,$mainCfg):PDO{return new PDO($mainDsn,(string)$mainCfg['username'],(string)$mainCfg['password'],[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_CASE=>PDO::CASE_LOWER]);};$aiConnect=static function()use($aiDsn,$aiCfg):PDO{return new PDO($aiDsn,(string)$aiCfg['username'],(string)$aiCfg['password'],[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_CASE=>PDO::CASE_LOWER]);};$main=$mainConnect();$aidb=$aiConnect();
$rows=static function(PDO$p,string$sql,array$args=[],int$cap=1000):array{$s=$p->prepare($sql);$s->execute($args);$out=[];while(($r=$s->fetch(PDO::FETCH_ASSOC))!==false){if(count($out)>=$cap)throw new RuntimeException('row cap');$clean=[];foreach(array_change_key_case($r,CASE_LOWER)as$k=>$v)$clean[$k]=is_resource($v)?stream_get_contents($v):$v;ksort($clean);$out[]=$clean;}return$out;};
$count=static function(PDO$p,string$sql,array$args=[])use($rows):int{$r=$rows($p,$sql,$args,1);return(int)(reset($r[0])?:0);};
$required=[$rule=>['RULE_ID','RULE_NAME','PAY_METHODS','MIN_MONEY','GIFT_MONEY','STATUS','STORE_ID','IS_DELETE','CREATE_BY','UPDATE_BY','CREATE_TIME','UPDATE_TIME'],$flow=>['LOG_ID','USER_ID','SCENE','MONEY','BALANCE_BEFORE','BALANCE_AFTER','BIZ_TYPE','BIZ_ORDER_ID','BIZ_ORDER_NO','DESCRIBE','REMARK','OPERATOR_NAME','STORE_ID','CREATE_TIME'],$staff=>['ID','AI_USER_ID','NAME','MOBILE','IS_SHOW'],$user=>['ID','UUID','ACCOUNT']];$metadata=[];
foreach($required as$table=>$columns){$db=strpos($table,'YOSHOP_')===0||strpos($table,'yoshop_')===0?$aidb:$main;if($lane==='B'){$s=$db->prepare('SELECT COLUMN_NAME,DATA_TYPE,COLUMN_TYPE,IS_NULLABLE,COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=? ORDER BY ORDINAL_POSITION');$s->execute([$table]);}else{$s=$db->prepare('SELECT COLUMN_NAME,DATA_TYPE,DATA_LENGTH,DATA_PRECISION,DATA_SCALE,NULLABLE,DATA_DEFAULT FROM USER_TAB_COLUMNS WHERE TABLE_NAME=? ORDER BY COLUMN_ID');$s->execute([$table]);}$names=[];foreach($s->fetchAll(PDO::FETCH_ASSOC)as$r){$names[]=strtoupper((string)$r['column_name']);$metadata[]=[$table,$r];}foreach($columns as$c)if(!in_array($c,$names,true))throw new RuntimeException('Required column absent');}
$snapshot=static function(PDO$ai,PDO$main)use($rows,$rule,$flow,$staff):array{$rules=$rows($ai,'SELECT * FROM '.$rule.' WHERE STORE_ID=? ORDER BY RULE_ID',[10001]);$flows=$rows($ai,'SELECT * FROM '.$flow.' WHERE STORE_ID=? ORDER BY LOG_ID',[10001]);$ids=[];foreach($flows as$r)if((int)$r['user_id']>0)$ids[(int)$r['user_id']]=true;$staffRows=[];if($ids){$marks=implode(',',array_fill(0,count($ids),'?'));$staffRows=$rows($main,'SELECT ID,AI_USER_ID,NAME,MOBILE,IS_SHOW FROM '.$staff.' WHERE AI_USER_ID IN ('.$marks.') ORDER BY ID',array_keys($ids));}return['rules'=>$rules,'flows'=>$flows,'staff'=>$staffRows];};
$source=$snapshot($aidb,$main);$sourceSha=hash('sha256',serialize($source));$ruleRows=array_values(array_filter($source['rules'],static function($r){return(int)$r['is_delete']===0;}));$flowRows=$source['flows'];$active=array_values(array_filter($ruleRows,static function($r){return(int)$r['status']===1;}));usort($active,static function($a,$b){$c=(float)$b['min_money']<=>(float)$a['min_money'];if($c)return$c;$c=(float)$b['gift_money']<=>(float)$a['gift_money'];return$c?:((int)$b['rule_id']<=>(int)$a['rule_id']);});$allowed=['cash','alipay_offline','wechat_offline','online_scan'];$candidate=null;$payMethod='';foreach($active as$r){$methods=json_decode((string)$r['pay_methods'],true);if(!is_array($methods))continue;foreach($methods as$m)if(in_array($m,$allowed,true)){$candidate=$r;$payMethod=$m;break 2;}}
$marker='CDXGIFT'.strtoupper(bin2hex(random_bytes(5)));$markerRows=$count($main,'SELECT COUNT(*) FROM '.$user." WHERE UUID LIKE 'CDXGIFT%' OR ACCOUNT LIKE 'CDXGIFT%'");if($markerRows!==0)throw new RuntimeException('Marker residue');
if($mode==='--preflight'){echo json_encode(['status'=>$candidate&&$ruleRows&&$flowRows?'natural_ready':'natural_insufficient','lane'=>$lane,'metadata_sha'=>hash('sha256',serialize($metadata)),'rule_rows'=>count($ruleRows),'active_rows'=>count($active),'flow_rows'=>count($flowRows),'staff_rows'=>count($source['staff']),'preview_candidate'=>$candidate!==null,'marker_rows'=>0,'source_sha'=>$sourceSha,'http_sent'=>false,'dml'=>false]),PHP_EOL;exit($candidate&&$ruleRows&&$flowRows?0:3);}
if($mode==='--audit'){$receipt=(string)getenv('CDX_GIFT_RECEIPT');if(!preg_match('~^/tmp/cdx-gift-[bc]-[a-f0-9]{12}/receipt\.json$~D',$receipt)||!is_file($receipt)||is_link($receipt)||is_link(dirname($receipt))||(fileperms(dirname($receipt))&0777)!==0700||(fileperms($receipt)&0777)!==0600)throw new RuntimeException('Receipt');$saved=json_decode((string)file_get_contents($receipt),true);if(!is_array($saved)||($saved['lane']??null)!==$lane||($saved['phase']??null)!=='http_complete'||($saved['source_sha']??null)!==$sourceSha)throw new RuntimeException('Audit mismatch');if($count($main,'SELECT COUNT(*) FROM '.$user." WHERE UUID LIKE 'CDXGIFT%' OR ACCOUNT LIKE 'CDXGIFT%'")!==0)throw new RuntimeException('Marker');unlink($receipt);rmdir(dirname($receipt));echo json_encode(['status'=>'independent_readonly_pass','lane'=>$lane,'source_unchanged'=>true,'marker_rows'=>0,'private_receipt_removed'=>true]),PHP_EOL;exit(0);}
if(!$candidate||!$ruleRows||!$flowRows)throw new RuntimeException('Natural source insufficient');$dir='/tmp/cdx-gift-'.strtolower($lane).'-'.strtolower(bin2hex(random_bytes(6)));if(!mkdir($dir,0700)||!chmod($dir,0700))throw new RuntimeException('Private dir');$receipt=$dir.'/receipt.json';$saved=['lane'=>$lane,'phase'=>'before_http','source_sha'=>$sourceSha];$save=static function()use(&$saved,$receipt):void{$e=json_encode($saved);if(file_put_contents($receipt,$e,LOCK_EX)!==strlen($e)||!chmod($receipt,0600))throw new RuntimeException('Receipt write');};$save();
require_once$root.'/application/base/Security.php';$token=\app\base\Security::encrypt(implode("\t",['2147480011',$marker,$marker,'0','','',$marker]));$shape=null;$shape=static function($v)use(&$shape){if(!is_array($v))return gettype($v);if($v===[])return[];$keys=array_keys($v);if($keys===range(0,count($v)-1))return['[]'=>$shape($v[0])];$out=[];foreach($v as$k=>$x)$out[(string)$k]=$shape($x);ksort($out);return$out;};$request=static function(string$path,array$form,?string$token)use($base,$runtimeReady):array{if(!$runtimeReady())throw new RuntimeException('Boundary');$body=http_build_query($form,'','&',PHP_QUERY_RFC3986);$headers="Content-Type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($body)."\r\n".($token===null?'':'token: '.$token."\r\n");$stream=@fopen($base.$path,'rb',false,stream_context_create(['http'=>['method'=>'POST','header'=>$headers,'content'=>$body,'timeout'=>20,'ignore_errors'=>true]]));$raw=is_resource($stream)?stream_get_contents($stream,1048577):false;$meta=is_resource($stream)?stream_get_meta_data($stream):[];$hs=$http_response_header??[];if(is_resource($stream))fclose($stream);preg_match('/HTTP\/\S+\s+(\d+)/',(string)($hs[0]??''),$m);$within=is_string($raw)&&strlen($raw)<=1048576;$json=$within?json_decode($raw,true):null;if(!isset($m[1])||(int)$m[1]!==200||!empty($meta['timed_out'])||!$within||!is_array($json)||json_last_error()!==JSON_ERROR_NONE||!array_key_exists('code',$json))throw new RuntimeException('HTTP unknown');return$json;};$cases=['giftRuleList'=>['/p/AccountSetting/giftRuleList',['page'=>1,'page_size'=>100]],'giftRuleDetail'=>['/p/AccountSetting/giftRuleDetail',['rule_id'=>(int)$candidate['rule_id']]],'giftRulePreview'=>['/p/AccountSetting/giftRulePreview',['money'=>(string)$candidate['min_money'],'pay_method'=>$payMethod]],'giftFlowList'=>['/p/AccountSetting/giftFlowList',['page'=>1,'page_size'=>100]]];$result=['status'=>'FAIL','lane'=>$lane,'cases'=>[],'rejections'=>[]];
foreach($cases as$name=>$case){$unauth=$request($case[0],$case[1],null);if((int)($unauth['code']??0)===0)throw new RuntimeException('Unauth accepted');$result['rejections'][$name.'.unauth']=true;$body=$request($case[0],$case[1],$token);if((int)($body['code']??-1)!==0||!is_array($body['data']??null))throw new RuntimeException('Business read failed');$data=$body['data'];if($name==='giftRuleList'){if((int)($data['total']??-1)!==count($ruleRows)||count($data['data']??[])!==count($ruleRows))throw new RuntimeException('Rule list mismatch');}elseif($name==='giftRuleDetail'){if((int)($data['rule_id']??0)!==(int)$candidate['rule_id']||!is_array($data['pay_methods']??null))throw new RuntimeException('Rule detail mismatch');}elseif($name==='giftRulePreview'){if((int)($data['rule_id']??0)!==(int)$candidate['rule_id']||number_format((float)($data['gift_money']??-1),2,'.','')!==number_format((float)$candidate['gift_money'],2,'.',''))throw new RuntimeException('Preview mismatch');}else{if((int)($data['total']??-1)!==count($flowRows)||count($data['data']??[])!==count($flowRows))throw new RuntimeException('Flow list mismatch');$income=0.0;$expense=0.0;foreach($flowRows as$r){$m=(float)$r['money'];if($m>0)$income+=$m;if($m<0)$expense+=$m;}if((string)($data['income_amount']??'')!==number_format($income,2,'.','')||(string)($data['expense_amount']??'')!==number_format(abs($expense),2,'.',''))throw new RuntimeException('Flow sums mismatch');}$result['cases'][$name]=['source_match'=>true,'key_sha'=>hash('sha256',serialize(array_keys($body))),'type_sha'=>hash('sha256',serialize($shape($body))),'shape'=>$shape($body)];}
$after=$snapshot($aiConnect(),$mainConnect());if(hash('sha256',serialize($after))!==$sourceSha)throw new RuntimeException('Read endpoints mutated source');if($count($mainConnect(),'SELECT COUNT(*) FROM '.$user." WHERE UUID LIKE 'CDXGIFT%' OR ACCOUNT LIKE 'CDXGIFT%'")!==0)throw new RuntimeException('Token marker persisted');$saved['phase']='http_complete';$save();$result['status']='HTTP_PASS_READONLY_PENDING_AUDIT';$result['source_sha']=$sourceSha;$result['receipt']=$receipt;echo json_encode($result,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),PHP_EOL;
