<?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:1831[12]$#',$base))exit(78);
$tenant=1930101010;$goods=[1930101011,1930101012];$marker='CDXSP10D6F8B2A147C9';
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();\think\App::$store_id=$tenant;
$app->config->set(['default'=>'file','stores'=>['file'=>['type'=>'File','path'=>$runtime.'/cache/','prefix'=>'','expire'=>0]]],'cache');
$newPdo=static function()use($dm):PDO{$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';return new PDO($dsn,getenv('POC_AI_USER'),getenv('POC_AI_PASSWORD'),[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_CASE=>PDO::CASE_LOWER]);};
$pdo=$newPdo();$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;};
$execute=static function($sql,$args=[])use($pdo):int{$s=$pdo->prepare($sql);$s->execute($args);return $s->rowCount();};
$hash=static function($v):string{return hash('sha256',json_encode($v,JSON_UNESCAPED_UNICODE));};
$snapshot=static function()use($q,$table,$hash):array{$o=[];foreach(['goods_spec_rel','spec','spec_value'] as $t){$rows=$q('SELECT * FROM '.$table($t));$h=array_map($hash,$rows);sort($h);$o[$t]=['rows'=>count($rows),'sha256'=>$hash($h)];}return $o;};
$before=$snapshot();
foreach(['goods_spec_rel','spec','spec_value','store'] as $t)if((int)$q('SELECT COUNT(*) AS N FROM '.$table($t).' WHERE STORE_ID=?',[$tenant])[0]['n']!==0)throw new RuntimeException('Synthetic tenant collides with existing records');
foreach(['goods','goods_spec_rel'] as $t)if((int)$q('SELECT COUNT(*) AS N FROM '.$table($t).' WHERE GOODS_ID IN (?,?)',$goods)[0]['n']!==0)throw new RuntimeException('Synthetic goods ID collides');
foreach(['goods_spec_rel','spec','spec_value'] as $t){$n=$table($t);$triggers=$dm?$q("SELECT TRIGGER_NAME FROM USER_TRIGGERS WHERE TABLE_NAME='".$n."'"):$q("SHOW TRIGGERS WHERE `Table`='".$n."'");$fk=$dm?$q("SELECT CONSTRAINT_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME='".$n."' AND CONSTRAINT_TYPE='R'"):$q("SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='".$n."' AND REFERENCED_TABLE_NAME IS NOT NULL");if($triggers||$fk)throw new RuntimeException('Approved FK/trigger boundary changed');}
$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('POST not blocked');
file_put_contents($runtime.'/ai_queries.jsonl','');chmod($runtime.'/ai_queries.jsonl',0600);
$token=bin2hex(random_bytes(24));$caseResults=[];$responses=[];$maps=['tenant'=>$tenant,'goods'=>$goods,'marker'=>$marker,'spec_ids'=>[],'value_ids'=>[],'relations'=>[]];$committed=false;$cleanup=[];$fixtureProof=[];
$expected=static function($goodsId)use($q,$table,$tenant):array{$rows=$q('SELECT * FROM '.$table('goods_spec_rel').' WHERE STORE_ID=? AND GOODS_ID=?',[$tenant,$goodsId]);$g=[];foreach($rows as $r){$sid=(int)$r['spec_id'];if(!isset($g[$sid])){$s=$q('SELECT * FROM '.$table('spec').' WHERE STORE_ID=? AND SPEC_ID=?',[$tenant,$sid]);if(count($s)!==1)throw new RuntimeException('Owned group association lost');$g[$sid]=['spec_id'=>$sid,'spec_name'=>$s[0]['spec_name'],'key'=>count($g),'valueList'=>[]];}$v=$q('SELECT * FROM '.$table('spec_value').' WHERE STORE_ID=? AND SPEC_VALUE_ID=? AND SPEC_ID=?',[$tenant,$r['spec_value_id'],$sid]);if(count($v)!==1)throw new RuntimeException('Owned value association lost');$g[$sid]['valueList'][]=['key'=>count($g[$sid]['valueList']),'groupKey'=>$g[$sid]['key'],'spec_value_id'=>(int)$v[0]['spec_value_id'],'spec_value'=>$v[0]['spec_value']];}return array_values($g);};
$wireShape=static function($v)use(&$wireShape){if(is_object($v)){$p=[];foreach(get_object_vars($v) as $k=>$x)$p[$k]=$wireShape($x);ksort($p);return ['type'=>'object','properties'=>$p];}if(is_array($v))return ['type'=>'array','items'=>array_map($wireShape,$v)];if(is_int($v)||is_float($v))return ['type'=>'number'];return ['type'=>gettype($v)];};
$request=static function($name,$module,$goodsId)use($base,$token,$expected,$wireShape,&$caseResults,&$responses):void{$path='/'.$module.'/goods.Spec/list';$raw=file_get_contents($base.$path.'?'.http_build_query(['goodsId'=>$goodsId]),false,stream_context_create(['http'=>['timeout'=>20,'ignore_errors'=>true,'header'=>"Platform: POC\r\nAccess-Token: ".$token."\r\nX-Pilot-Scenario: PILOT_AI_SPEC_OWNED_".$name."\r\n"]]));preg_match('#^HTTP/\S+\s+(\d+)#',$http_response_header[0]??'',$m);$body=json_decode((string)$raw,true);$source=$expected($goodsId);$list=$body['data']['list']??null;$equal=$list===$source;$values=0;foreach($source as $g)$values+=count($g['valueList']);$caseResults[]=['scenario'=>$name,'method'=>'GET','path'=>$path,'args'=>['goodsId'=>$goodsId],'groups'=>count($source),'values'=>$values,'source_equal'=>$equal,'pass'=>(int)($m[1]??0)===200&&($body['status']??null)===200&&$equal];$responses[$name]=['http_status'=>(int)($m[1]??0),'body'=>json_decode((string)$raw),'wire_shape'=>$wireShape(json_decode((string)$raw))];};
try{
    $pdo->beginTransaction();$now=time();
    foreach(['颜色 \'组\'','尺寸 "组"'] as $key=>$label){$name=$marker.'_'.$label;$execute('INSERT INTO '.$table('spec').' (SPEC_NAME,STORE_ID,CREATE_TIME) VALUES (?,?,?)',[$name,$tenant,$now]);$row=$q('SELECT * FROM '.$table('spec').' WHERE STORE_ID=? AND SPEC_NAME=?',[$tenant,$name]);if(count($row)!==1||$row[0]['spec_name']!==$name)throw new RuntimeException('Owned group prepare/readback failed');$maps['spec_ids'][(string)$key]=(int)$row[0]['spec_id'];}
    $valueDefs=[[0,'红色 "原样"'],[0,"蓝色 '原样'"],[0,'中文 \\ 值'],[1,'小号'],[1,'大号']];
    foreach($valueDefs as $key=>[$group,$label]){$name=$marker.'_'.$label;$sid=$maps['spec_ids'][(string)$group];$execute('INSERT INTO '.$table('spec_value').' (SPEC_VALUE,SPEC_ID,STORE_ID,CREATE_TIME) VALUES (?,?,?,?)',[$name,$sid,$tenant,$now]);$row=$q('SELECT * FROM '.$table('spec_value').' WHERE STORE_ID=? AND SPEC_ID=? AND SPEC_VALUE=?',[$tenant,$sid,$name]);if(count($row)!==1||$row[0]['spec_value']!==$name)throw new RuntimeException('Owned value prepare/readback failed');$maps['value_ids'][(string)$key]=(int)$row[0]['spec_value_id'];}
    foreach([[0,0],[0,3],[0,1],[0,4],[0,2],[1,4],[1,3]] as [$good,$value]){$group=$valueDefs[$value][0];$execute('INSERT INTO '.$table('goods_spec_rel').' (GOODS_ID,SPEC_ID,SPEC_VALUE_ID,STORE_ID,CREATE_TIME) VALUES (?,?,?,?,?)',[$goods[$good],$maps['spec_ids'][(string)$group],$maps['value_ids'][(string)$value],$tenant,$now]);}
    $relations=$q('SELECT * FROM '.$table('goods_spec_rel').' WHERE STORE_ID=? ORDER BY ID',[$tenant]);if(count($relations)!==7)throw new RuntimeException('Owned relation count mismatch');$maps['relations']=array_map('intval',array_column($relations,'id'));
    $pdo->commit();$committed=true;
    $fixtureProof=['groups'=>count($maps['spec_ids']),'values'=>count($maps['value_ids']),'relations'=>count($relations),'goods_rows'=>0,'store_rows'=>0];
    \think\facade\Cache::set($token,['user'=>['store_id'=>$tenant,'store_user_id'=>1930101013,'real_name'=>$marker,'user_name'=>$marker,'is_super'=>1],'store_id'=>$tenant,'is_login'=>true],3600);
    foreach(['terminal','store'] as $module){$request(strtoupper($module).'_FULL',$module,$goods[0]);$request(strtoupper($module).'_REVERSED_VALUES',$module,$goods[1]);$request(strtoupper($module).'_ZERO',$module,0);}
}finally{
    if($pdo->inTransaction())$pdo->rollBack();
    if($committed){
        $pdo->beginTransaction();
        $cleanup['relations']=$execute('DELETE FROM '.$table('goods_spec_rel').' WHERE STORE_ID=? AND GOODS_ID IN (?,?) AND ID IN ('.implode(',',array_fill(0,count($maps['relations']),'?')).')',array_merge([$tenant],$goods,$maps['relations']));
        $cleanup['values']=$execute('DELETE FROM '.$table('spec_value').' WHERE STORE_ID=? AND SPEC_VALUE LIKE ? AND SPEC_VALUE_ID IN ('.implode(',',array_fill(0,count($maps['value_ids']),'?')).')',array_merge([$tenant,$marker.'%'],array_values($maps['value_ids'])));
        $cleanup['groups']=$execute('DELETE FROM '.$table('spec').' WHERE STORE_ID=? AND SPEC_NAME LIKE ? AND SPEC_ID IN ('.implode(',',array_fill(0,count($maps['spec_ids']),'?')).')',array_merge([$tenant,$marker.'%'],array_values($maps['spec_ids'])));
        $pdo->commit();
    }
}
foreach(['terminal','store'] as $module){$request(strtoupper($module).'_CLEANED_FULL',$module,$goods[0]);$request(strtoupper($module).'_CLEANED_SECOND',$module,$goods[1]);}
\think\facade\Cache::delete($token);\think\facade\Cache::clear();$audit=$newPdo();$remaining=[];
foreach(['goods_spec_rel','spec','spec_value','store'] as $t){$s=$audit->prepare('SELECT COUNT(*) FROM '.$table($t).' WHERE STORE_ID=?');$s->execute([$tenant]);$remaining[$t]=(int)$s->fetchColumn();}
$s=$audit->prepare('SELECT COUNT(*) FROM '.$table('goods').' WHERE GOODS_ID IN (?,?)');$s->execute($goods);$remaining['goods']=(int)$s->fetchColumn();
$after=$snapshot();$events=[];foreach(file($runtime.'/ai_queries.jsonl',FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)?:[] as $line)$events[]=json_decode($line,true);$httpDml=0;foreach($events as $e)if(in_array($e['operation']??'',['insert','update','delete','replace','merge'],true))++$httpDml;
$sha=[];foreach(['app/store/controller/goods/Spec.php','app/terminal/controller/goods/Spec.php','app/store/model/GoodsSpecRel.php','app/terminal/model/GoodsSpecRel.php','app/common/model/GoodsSpecRel.php','app/common/model/Spec.php','app/common/model/SpecValue.php','cores/BaseModel.php'] as $f)$sha[$f]=hash_file('sha256',$overlay.'/'.$f);
$report=['lane'=>$lane,'baseline'=>'d412aeff9bfb828f56a5f87ca24acd4e029efa40','cases'=>$caseResults,'case_count'=>count($caseResults),'failures'=>count(array_filter($caseResults,static function($c){return !$c['pass'];})),'fixture_proof'=>$fixtureProof,'cleanup'=>$cleanup,'independent_remaining'=>$remaining,'before'=>$before,'after'=>$after,'natural_unchanged'=>$before===$after,'http_dml_observed'=>$httpDml,'token_cleaned'=>!\think\facade\Cache::has($token),'source_sha256'=>$sha,'events'=>$events,'boundary_verified'=>true,'scope'=>'Only three-table relation-reading fixture; not actual Goods/Store whole business'];
foreach(['report.json'=>$report,'responses.json'=>$responses,'fixture_map.json'=>$maps] as $file=>$data){file_put_contents($runtime.'/'.$file,json_encode($data,JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES));chmod($runtime.'/'.$file,0600);}
echo json_encode(['lane'=>$lane,'cases'=>count($caseResults),'failures'=>$report['failures'],'unchanged'=>$before===$after,'remaining'=>$remaining,'http_dml'=>$httpDml,'token_cleaned'=>$report['token_cleaned'],'runtime'=>$runtime]),"\n";exit($report['failures']||$before!==$after||array_sum($remaining)||$httpDml?1:0);
