<?php
declare(strict_types=1);
date_default_timezone_set('Asia/Shanghai');
$lane=(string)getenv('CATEGORY_LANE');$base=(string)getenv('CATEGORY_BASE');$runtime=(string)getenv('CATEGORY_RUNTIME');
if(!in_array($lane,['B','C'],true)||!preg_match('#^http://127\.0\.0\.1:1823[12]$#D',$base))exit(78);
$dm=getenv('POC_DIALECT')==='dm';
$connect=static function()use($dm):PDO{return new PDO($dm?'dm:host=127.0.0.1;port=5238;schema=AIZHCT_APP':
    'mysql:host='.getenv('POC_AI_HOST').';port='.getenv('POC_AI_PORT').';dbname='.getenv('POC_AI_SCHEMA').';charset=utf8',
    getenv('POC_AI_USER'),getenv('POC_AI_PASSWORD'),[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_CASE=>PDO::CASE_LOWER]);};
$db=$connect();
if($dm){if($db->query("SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn()!=='AIZHCT_APP')throw new RuntimeException('DM identity');}
else{if(strpos((string)$db->query('SELECT VERSION()')->fetchColumn(),'5.6.')!==0
    ||$db->query('SELECT DATABASE()')->fetchColumn()!==getenv('POC_AI_SCHEMA'))throw new RuntimeException('MySQL local identity');}
$q=static function(string $sql,array $args=[])use($db):array{$s=$db->prepare($sql);$s->execute($args);return $s->fetchAll(PDO::FETCH_ASSOC);};
$prefix='CDXCAT10B7F93';
$own=static function()use($q,$prefix):array{return $q('SELECT * FROM yoshop_category WHERE name LIKE ? AND store_id=10001 ORDER BY category_id',[$prefix.'%']);};
if(($argv[1]??'')==='audit'){echo json_encode(['lane'=>$lane,'independent_owned_rows'=>count($own())]),"\n";exit(count($own())?1:0);}
$boundary=json_decode((string)file_get_contents($base.'/__poc_boundary'),true);
if(empty($boundary['external_stubs'])||empty($boundary['private_cache']))throw new RuntimeException('Boundary');
if($own())throw new RuntimeException('Fixture collision');
$before=$q('SELECT * FROM yoshop_category WHERE store_id=10001 ORDER BY category_id');
$beforeHash=hash('sha256',json_encode($before));
define('TP_ENV','poc');define('BUSINESS','aizhct');define('ENTERPRISE_NO','10001');
$root=(string)getenv('CATEGORY_ROOT');require $root.'/vendor/autoload.php';
$app=new \think\App($root);$app->setEnvName('poc');$app->setRuntimePath($runtime.'/fixture-runtime/');$app->initialize();
$cache=new \think\cache\driver\File($app,['path'=>$runtime.'/cache/','prefix'=>'','expire'=>0]);
$token=bin2hex(random_bytes(24));$cache->set($token,['user'=>['store_user_id'=>2147000010,'store_id'=>10001,
    'real_name'=>$prefix,'user_name'=>$prefix,'is_super'=>1],'store_id'=>10001,'is_login'=>true],1800);
$cases=[];$ids=[];$updatedTimes=[];$formEncodingScenarios=[];$error=null;$responseBodies=[];
file_put_contents($runtime.'/queries.jsonl','');chmod($runtime.'/queries.jsonl',0600);
$request=static function(string $appName,string $case,string $action,array $args=[],array $form=[],string $encoding='json')use($base,$token,&$responseBodies):array{
    $scenario='PILOT_CAT_'.strtoupper($appName).'_'.$case;$method=$action==='list'?'GET':'POST';
    $url=$base.'/'.$appName.'/Category/'.$action.($args?'?'.http_build_query($args):'');
    $contentType=$encoding==='json'?'application/json':'application/x-www-form-urlencoded';
    $content=$encoding==='json'?json_encode(['form'=>$form],JSON_UNESCAPED_UNICODE):http_build_query(['form'=>$form]);
    $headers="Access-Token: ".$token."\r\nPlatform: POC\r\nX-Pilot-Scenario: ".$scenario."\r\nContent-Type: ".$contentType."\r\n";
    $raw=file_get_contents($url,false,stream_context_create(['http'=>['method'=>$method,'header'=>$headers,
        'content'=>$content,'timeout'=>30,'ignore_errors'=>true]]));
    preg_match('#^HTTP/\S+\s+(\d+)#',$http_response_header[0]??'',$matches);
    $body=json_decode((string)$raw,true);
    if(!is_array($body))throw new RuntimeException('Non JSON '.$scenario);
    $responseBodies[$scenario]=['http_status'=>(int)($matches[1]??0),'body'=>$body];
    return ['scenario'=>$scenario,'method'=>$method,'path'=>'/'.$appName.'/Category/'.$action,
        'http_status'=>(int)($matches[1]??0),'body'=>$body];
};
$assert=static function(bool $ok,string $message):void{if(!$ok)throw new RuntimeException($message);};
try{
foreach(['store','terminal'] as $appName){
    $name=$prefix.'_'.$appName.'_餐牌\'\\';$child=$prefix.'_'.$appName.'_子';$edited=$prefix.'_'.$appName.'_改';
    $form=['name'=>$name,'parent_id'=>0,'image_id'=>0,'status'=>1,'sort'=>2147000010,'create_time'=>1609459200,'update_time'=>1609459200];
    $case=$request($appName,'ADD_ROOT','add',[],$form);
    $rows=$q('SELECT * FROM yoshop_category WHERE name=? AND store_id=10001',[$name]);
    $assert($case['http_status']===200&&$case['body']['status']===200&&count($rows)===1,'Root add');
    $rootId=(int)$rows[0]['category_id'];$ids[$rootId]=1;
    $assert((int)$rows[0]['parent_id']===0&&(int)$rows[0]['image_id']===0&&(int)$rows[0]['status']===1,'Root original state');
    $case['independent_state']=['rows'=>1,'parent_id'=>0,'image_id'=>0,'status'=>1,'name_equal'=>true];$cases[]=$case;
    $childForm=$form;$childForm['name']=$child;$childForm['parent_id']=$rootId;$childForm['sort']=2147000011;
    $case=$request($appName,'ADD_CHILD','add',[],$childForm);
    $rows=$q('SELECT * FROM yoshop_category WHERE name=? AND store_id=10001',[$child]);
    $assert($case['body']['status']===200&&count($rows)===1&&(int)$rows[0]['parent_id']===$rootId,'Child original state');
    $childId=(int)$rows[0]['category_id'];$ids[$childId]=2;
    $case['independent_state']=['rows'=>1,'parent_id_semantic'=>'OWN_ROOT','status'=>1];$cases[]=$case;
    $case=$request($appName,'LIST_TREE','list');
    $matches=array_values(array_filter($case['body']['data']['list']??[],static function($v)use($rootId):bool{return (int)($v['category_id']??0)===$rootId;}));
    $assert($case['body']['status']===200&&count($matches)===1&&count($matches[0]['children']??[])===1,'Tree owned projection');
    $assert((int)$matches[0]['children'][0]['category_id']===$childId,'Child tree state');
    $case['body']['data']['list']=$matches;$case['projection']='only independently verified owned subtree';$cases[]=$case;
    $case=$request($appName,'DELETE_PARENT_REJECT','delete',['categoryId'=>$rootId]);
    $assert($case['body']['status']===500&&count($own())===2,'Parent rejection state');$cases[]=$case;
    $cycle=$form;$cycle['parent_id']=$childId;
    $case=$request($appName,'CYCLE_REJECT','edit',['categoryId'=>$rootId],$cycle);
    $rows=$q('SELECT parent_id FROM yoshop_category WHERE category_id=?',[$rootId]);
    $assert($case['body']['status']===500&&$case['body']['message']==='上级分类不允许设置为当前子分类'
        &&(int)$rows[0]['parent_id']===0,'Cycle rejection state');$cases[]=$case;
    $childForm['name']=$edited;$childForm['status']=0;$childForm['sort']=2147000012;unset($childForm['image_id']);
    $case=$request($appName,'EDIT_CHILD','edit',['categoryId'=>$childId],$childForm);
    $rows=$q('SELECT * FROM yoshop_category WHERE category_id=?',[$childId]);
    $assert($case['body']['status']===200&&count($rows)===1&&$rows[0]['name']===$edited
        &&(int)$rows[0]['status']===0&&(int)$rows[0]['image_id']===0&&(int)$rows[0]['sort']===2147000012,'Edit original state');
    $updatedTimes[$childId]=date('Y-m-d H:i:s',(int)$rows[0]['update_time']);
    $case['independent_state']=['rows'=>1,'name_equal'=>true,'status'=>0,'image_id'=>0,'sort'=>2147000012];$cases[]=$case;
    $formPayload=$childForm;$formPayload['sort']=2147000013;
    $formCase=$request($appName,'FORM_PARENT_ID','edit',['categoryId'=>$childId],$formPayload,'form');
    $afterForm=$q('SELECT * FROM yoshop_category WHERE category_id=?',[$childId]);
    $assert($formCase['http_status']===200&&$formCase['body']['status']===200
        &&count($afterForm)===1&&(int)$afterForm[0]['sort']===2147000013
        &&(int)$afterForm[0]['parent_id']===$rootId,'Fixed form edit original state');
    $updatedTimes[$childId]=date('Y-m-d H:i:s',(int)$afterForm[0]['update_time']);
    $formCase['independent_state']=['sort'=>2147000013,'parent_id_semantic'=>'OWN_ROOT','rows'=>1];
    $formEncodingScenarios[]=$formCase;
    $formCycle=$request($appName,'FORM_CYCLE_REJECT','edit',['categoryId'=>$rootId],$cycle,'form');
    $cycleOriginal=$q('SELECT parent_id FROM yoshop_category WHERE category_id=?',[$rootId]);
    $assert($formCycle['http_status']===200&&$formCycle['body']['status']===500
        &&$formCycle['body']['message']==='上级分类不允许设置为当前子分类'
        &&count($cycleOriginal)===1&&(int)$cycleOriginal[0]['parent_id']===0,'Fixed form cycle rejection');
    $formCycle['independent_state']=['parent_id'=>0,'rows'=>1,'cycle_not_written'=>true];
    $formEncodingScenarios[]=$formCycle;
    $case=$request($appName,'LIST_EDITED','list');
    $matches=array_values(array_filter($case['body']['data']['list']??[],static function($v)use($rootId):bool{return (int)($v['category_id']??0)===$rootId;}));
    $assert(count($matches)===1&&($matches[0]['children'][0]['name']??'')===$edited
        &&($matches[0]['children'][0]['status']??-1)===0,'Edited list state');
    $assert(($matches[0]['children'][0]['update_time']??null)===$updatedTimes[$childId],'HTTP auto timestamp vs original database');
    $matches[0]['children'][0]['update_time']='VERIFIED_AUTO_UPDATE_TIME';
    $case['independent_update_time_verified']=true;
    $case['body']['data']['list']=$matches;$case['projection']='only independently verified owned subtree';$cases[]=$case;
    $case=$request($appName,'DELETE_CHILD','delete',['categoryId'=>$childId]);
    $assert($case['body']['status']===200&&!$q('SELECT category_id FROM yoshop_category WHERE category_id=?',[$childId]),'Child deletion');$cases[]=$case;
    $case=$request($appName,'DELETE_ROOT','delete',['categoryId'=>$rootId]);
    $assert($case['body']['status']===200&&!$own(),'Root deletion');$cases[]=$case;
}
}catch(Throwable $caught){$error=['class'=>get_class($caught),'message'=>$caught->getMessage(),'line'=>$caught->getLine()];}
finally{
    foreach($own() as $row){$s=$db->prepare('DELETE FROM yoshop_category WHERE category_id=? AND name LIKE ? AND store_id=10001');$s->execute([$row['category_id'],$prefix.'%']);}
    $cache->delete($token);
}
$after=$q('SELECT * FROM yoshop_category WHERE store_id=10001 ORDER BY category_id');
$fresh=$connect();$s=$fresh->prepare('SELECT COUNT(*) FROM yoshop_category WHERE name LIKE ? AND store_id=10001');$s->execute([$prefix.'%']);$freshOwn=(int)$s->fetchColumn();
$relationRows=0;foreach(array_keys($ids) as $id){$s=$fresh->prepare('SELECT COUNT(*) FROM yoshop_goods_category_rel WHERE category_id=?');$s->execute([$id]);$relationRows+=(int)$s->fetchColumn();}
$cleanup=['fresh_pdo_owned_rows'=>$freshOwn,'owned_relation_rows'=>$relationRows,'token_absent'=>$cache->get($token)===null,
    'existing_categories_unchanged'=>hash('sha256',json_encode($after))===$beforeHash];
$normalise=static function($value)use(&$normalise,$ids){
    if(!is_array($value))return $value;
    foreach($value as $key=>&$part){
        if(($key==='category_id'||$key==='parent_id')&&is_int($part)&&isset($ids[$part]))$part=$ids[$part];
        else $part=$normalise($part);
    }unset($part);return $value;};
foreach($cases as &$case){
    $trace=$case['body']['trace_id']??null;$case['trace_format_valid']=is_string($trace)&&preg_match('/^[0-9a-f]{13}$/D',$trace)===1;
    if($case['trace_format_valid'])$case['body']['trace_id']='VALID_DYNAMIC_TRACE';
    $case=$normalise($case);
}unset($case);
foreach($formEncodingScenarios as &$case){
    $trace=$case['body']['trace_id']??null;$case['trace_format_valid']=is_string($trace)&&preg_match('/^[0-9a-f]{13}$/D',$trace)===1;
    if($case['trace_format_valid'])$case['body']['trace_id']='VALID_DYNAMIC_TRACE';
    $case=$normalise($case);
}unset($case);
$events=[];foreach(file($runtime.'/queries.jsonl',FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)?:[] as $line)$events[]=json_decode($line,true);
$report=['lane'=>$lane,'boundary_verified'=>true,'case_count'=>count($cases),'cases'=>$cases,'cleanup'=>$cleanup,
    'form_encoding_scenarios'=>$formEncodingScenarios,
    'error'=>$error,'events'=>$events,'normalization'=>'validated trace; independently verified category_id/parent_id integers; own edited-child update_time checked against original DB only',
    'limitations'=>'List full natural data differs by snapshot; only owned subtree is compared; no goods-reference rejection fixture, real login or arbitrary malformed requests tested'];
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.'/private-responses.json',json_encode($responseBodies,JSON_UNESCAPED_UNICODE));chmod($runtime.'/private-responses.json',0600);
echo json_encode(['lane'=>$lane,'cases'=>count($cases),'form_cases'=>count($formEncodingScenarios),
    'total_http_scenarios'=>count($cases)+count($formEncodingScenarios),
    'cleanup'=>$cleanup,'error'=>$error,'report'=>$runtime.'/report.json']),"\n";
exit($error!==null||$freshOwn!==0||$relationRows!==0||in_array(false,$cleanup,true)?1:0);
