<?php
declare(strict_types=1);
date_default_timezone_set('Asia/Shanghai');
$lane=getenv('PILOT_LANE');$root=getenv('PILOT_TARGET_ROOT');$runtime=getenv('PILOT_RUNTIME');$base=getenv('POC_BASE_URL');$dm=getenv('POC_DIALECT')==='dm';
if(!in_array($lane,['B','C'],true)||!preg_match('#^http://127\.0\.0\.1:1829[12]$#',$base))exit(78);
define('TP_ENV','poc');define('BUSINESS','aizhct');define('ENTERPRISE_NO','10001');
$loader=require $root.'/vendor/autoload.php';$overlay=getenv('TERMINAL_SOURCE_OVERLAY');$loader->setPsr4('app\\',$overlay.'/app');$loader->setPsr4('cores\\',$overlay.'/cores');require $root.'/external_stubs.php';
$app=new \think\App($root);$app->setEnvName('poc');$app->setRuntimePath($runtime.'/runner/');$app->initialize();$app->config->set(['default'=>'file','stores'=>['file'=>['type'=>'File','path'=>$runtime.'/cache/','prefix'=>'','expire'=>0]]],'cache');
$dsn=$dm?'dm:host='.getenv('POC_AI_HOST').';port='.getenv('POC_AI_PORT').';schema='.getenv('POC_AI_SCHEMA'):'mysql:host='.getenv('POC_AI_HOST').';port='.getenv('POC_AI_PORT').';dbname='.getenv('POC_AI_SCHEMA').';charset=utf8';
$pdo=new PDO($dsn,getenv('POC_AI_USER'),getenv('POC_AI_PASSWORD'),[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_CASE=>PDO::CASE_LOWER]);
$table=static function($n)use($dm):string{return $dm?strtoupper('yoshop_'.$n):'yoshop_'.$n;};
$q=static function($sql,$args=[])use($pdo):array{$s=$pdo->prepare($sql);$s->execute($args);$rows=$s->fetchAll(PDO::FETCH_ASSOC);foreach($rows as &$r)foreach($r as &$v)if(is_resource($v))$v=stream_get_contents($v);return $rows;};
$hash=static function($v):string{return hash('sha256',json_encode($v,JSON_UNESCAPED_UNICODE));};
$names=['region','goods_spec_rel','spec','spec_value'];$sources=[];foreach($names as $n)$sources[$n]=$q('SELECT * FROM '.$table($n));
$snapshot=static function()use($names,$q,$table,$hash):array{$o=[];foreach($names as $n){$rows=$q('SELECT * FROM '.$table($n));$hashes=array_map($hash,$rows);sort($hashes);$o[$n]=['rows'=>count($rows),'sha256'=>$hash($hashes)];}return $o;};
$before=$snapshot();$users=$q('SELECT * FROM '.$table('store_user').' WHERE STORE_ID=10001 AND IS_DELETE=0 AND IS_SUPER=1');if(!$users)throw new RuntimeException('No cache-only authentication fixture');
$user=$users[0];$user['store_id']=(int)$user['store_id'];$user['store_user_id']=(int)$user['store_user_id'];$token=bin2hex(random_bytes(24));\think\facade\Cache::set($token,['user'=>$user,'store_id'=>10001,'is_login'=>true],3600);
$boundary=json_decode(file_get_contents($base.'/__poc_boundary'),true);if(empty($boundary['external_stubs'])||empty($boundary['readonly_get_allowlist']))throw new RuntimeException('Boundary absent');
file_get_contents($base.'/terminal/goods.Spec/list',false,stream_context_create(['http'=>['method'=>'POST','ignore_errors'=>true]]));if(strpos($http_response_header[0]??'','403')===false)throw new RuntimeException('Write/method guard absent');
file_put_contents($runtime.'/ai_queries.jsonl','');chmod($runtime.'/ai_queries.jsonl',0600);
$cases=[];$responses=[];$byId=[];foreach($sources['region'] as $r)$byId[(int)$r['id']]=['id'=>(int)$r['id'],'pid'=>(int)$r['pid'],'name'=>$r['name'],'level'=>(int)$r['level']];
$request=static function($name,$path,$args=[])use($base,$token,&$responses):array{$raw=file_get_contents($base.$path.'?'.http_build_query(['storeId'=>10001]+$args),false,stream_context_create(['http'=>['ignore_errors'=>true,'timeout'=>30,'header'=>"Platform: POC\r\nAccess-Token: ".$token."\r\nX-Pilot-Scenario: PILOT_AI_REGION_SPEC_".$name."\r\n"]]));preg_match('#^HTTP/\S+\s+(\d+)#',$http_response_header[0]??'',$m);$r=['http_status'=>(int)($m[1]??0),'body'=>json_decode((string)$raw,true)];$responses[$name]=$r;return $r;};
$regionCheck=static function($list,$tree)use($byId):array{$seen=[];$okay=is_array($list)&&count($list)>0;$walk=static function($items,$level,$parent=null)use(&$walk,&$seen,&$okay,$byId):void{foreach($items as $key=>$item){if(!is_array($item)||!isset($item['id'])){$okay=false;continue;}$id=$item['id'];if(!is_int($id)||!isset($byId[$id])||isset($seen[$id])){$okay=false;continue;}$seen[$id]=true;$row=$byId[$id];foreach(['id','pid','name','level'] as $field)if(($item[$field]??null)!==$row[$field])$okay=false;if((int)$key!==$id||$row['level']!==$level||($parent!==null&&$row['pid']!==$parent))$okay=false;if(isset($item['city']))$walk($item['city'],2,$id);if(isset($item['region']))$walk($item['region'],3,$id);}};
    if($tree)$walk($list,1);else foreach($list as $key=>$item){$id=$item['id']??null;if(!is_int($id)||!isset($byId[$id])||(int)$key!==$id||$item!==$byId[$id]){$okay=false;continue;}$seen[$id]=true;}
    return [$okay&&count($seen)===count($byId),count($seen)];};
$specExpected=static function($goodsId)use($q,$table):array{$rel=$q('SELECT * FROM '.$table('goods_spec_rel').' WHERE STORE_ID=10001 AND GOODS_ID=?',[$goodsId]);$groups=[];$order=[];foreach($rel as $r){$id=(int)$r['spec_id'];if(!isset($groups[$id])){$s=$q('SELECT * FROM '.$table('spec').' WHERE STORE_ID=10001 AND SPEC_ID=?',[$id]);if(count($s)!==1)throw new RuntimeException('Unresolved natural spec group');$groups[$id]=['spec_id'=>$id,'spec_name'=>$s[0]['spec_name'],'key'=>count($groups),'valueList'=>[]];$order[]=$id;}$v=$q('SELECT * FROM '.$table('spec_value').' WHERE STORE_ID=10001 AND SPEC_VALUE_ID=?',[$r['spec_value_id']]);if(count($v)!==1)throw new RuntimeException('Unresolved natural spec value');$groups[$id]['valueList'][]=['key'=>count($groups[$id]['valueList']),'groupKey'=>$groups[$id]['key'],'spec_value_id'=>(int)$v[0]['spec_value_id'],'spec_value'=>$v[0]['spec_value']];}return array_values($groups);};
try{
    foreach(['api','store','terminal'] as $module)foreach(['all','tree'] as $action){
        \think\facade\Cache::delete('region');if(\think\facade\Cache::has('region'))throw new RuntimeException('Private cold cache failed');
        foreach(['COLD','WARM'] as $mode){$name=strtoupper($module).'_'.strtoupper($action).'_'.$mode;$path='/'.$module.'/Region/'.$action;$r=$request($name,$path);[$equal,$nodes]=$regionCheck($r['body']['data']['list']??null,$action==='tree');$cases[]=['scenario'=>$name,'method'=>'GET','path'=>$path,'expected_business_status'=>200,'source_equal'=>$equal,'source_rows'=>$nodes,'positive_rows'=>$nodes,'cache_mode'=>$mode,'cache_exists_after'=>\think\facade\Cache::has('region'),'args'=>[],'pass'=>$r['http_status']===200&&($r['body']['status']??null)===200&&$equal];}
    }
    foreach(['terminal','store'] as $module)foreach([10001,10003,0,-1,null] as $id){$name=strtoupper($module).'_SPEC_'.($id===null?'MISSING':str_replace('-','NEG',(string)$id));$path='/'.$module.'/goods.Spec/list';$args=$id===null?[]:['goodsId'=>$id];$r=$request($name,$path,$args);$expected=$id===null?null:$specExpected($id);$actual=$r['body']['data']['list']??null;$equal=$id===null?true:$actual===$expected;$status=$id===null?500:200;$values=0;foreach($expected??[] as $g)$values+=count($g['valueList']);$cases[]=['scenario'=>$name,'method'=>'GET','path'=>$path,'expected_business_status'=>$status,'source_equal'=>$equal,'source_rows'=>$values,'positive_rows'=>$values,'args'=>$args,'pass'=>$r['http_status']===200&&($r['body']['status']??null)===$status&&$equal];}
}finally{\think\facade\Cache::delete($token);\think\facade\Cache::clear();}
$after=$snapshot();$events=[];foreach(file($runtime.'/ai_queries.jsonl',FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)?:[] as $line)$events[]=json_decode($line,true);$dml=0;
foreach($events as $e)if(in_array($e['operation']??'',['insert','update','delete','replace','merge'],true))++$dml;
$metadata=[];foreach($events as $e)if(($e['kind']??'')==='metadata_fingerprint')$metadata[($e['scenario']??'').':'.$e['fingerprint']]=true;
foreach($cases as &$case){$regionQueries=0;foreach($events as $e)if(($e['scenario']??'')==='PILOT_AI_REGION_SPEC_'.$case['scenario']&&($e['operation']??'')==='select'&&!isset($metadata[($e['scenario']??'').':'.($e['fingerprint']??'')]))foreach($e['callsites']??[] as $site)if(strpos($site,'app/common/model/Region.php:')===0){++$regionQueries;break;}$case['region_query_observations']=$regionQueries;if(isset($case['cache_mode']))$case['pass']=$case['pass']&&($case['cache_mode']==='COLD'?$regionQueries>0:$regionQueries===0);}
unset($case);$files=[];foreach(['api','store','terminal'] as $module){foreach(['controller/Region.php','model/Region.php'] as $f)$files[]='app/'.$module.'/'.$f;}
foreach(['store','terminal'] as $module)foreach(['controller/goods/Spec.php','model/GoodsSpecRel.php','model/Spec.php','model/SpecValue.php'] as $f)$files[]='app/'.$module.'/'.$f;
$files=array_merge($files,['app/common/model/Region.php','app/common/model/GoodsSpecRel.php','app/common/model/Spec.php','app/common/model/SpecValue.php','cores/BaseModel.php']);$sha=[];foreach($files as $f)$sha[$f]=hash_file('sha256',$overlay.'/'.$f);
$report=['lane'=>$lane,'baseline'=>'d412aeff9bfb828f56a5f87ca24acd4e029efa40','case_count'=>count($cases),'cases'=>$cases,'failures'=>count(array_filter($cases,static function($c){return !$c['pass'];})),'before'=>$before,'after'=>$after,'natural_unchanged'=>$before===$after,'non_select_observed'=>$dml,'token_cleaned'=>!\think\facade\Cache::has($token),'region_cache_cleaned'=>!\think\facade\Cache::has('region'),'source_sha256'=>$sha,'events'=>$events,'boundary_verified'=>true,'scope'=>'specified natural read and private cache branches only'];
file_put_contents($runtime.'/report.json',json_encode($report,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));chmod($runtime.'/report.json',0600);file_put_contents($runtime.'/responses.json',json_encode($responses,JSON_UNESCAPED_UNICODE));chmod($runtime.'/responses.json',0600);
echo json_encode(['lane'=>$lane,'cases'=>count($cases),'failures'=>$report['failures'],'unchanged'=>$before===$after,'dml'=>$dml,'token_cleaned'=>$report['token_cleaned'],'region_cache_cleaned'=>$report['region_cache_cleaned'],'runtime'=>$runtime]),"\n";exit($report['failures']||$before!==$after||$dml?1:0);
