<?php
declare(strict_types=1);
date_default_timezone_set('Asia/Shanghai');
$lane=getenv('PILOT_LANE');$stage=getenv('UPLOAD_STAGE');$root=getenv('PILOT_TARGET_ROOT');$overlay=getenv('UPLOAD_SOURCE_OVERLAY');$runtime=getenv('PILOT_RUNTIME');$dm=getenv('POC_DIALECT')==='dm';$tenant=1930201010;$marker='CDXUF10D28B34A19F6C';
if(!in_array($lane,['B','C'],true)||!in_array($stage,['old','green'],true))exit(78);
define('TP_ENV','poc');define('BUSINESS','aizhct');define('ENTERPRISE_NO','10001');
$loader=require $root.'/vendor/autoload.php';$loader->setPsr4('app\\',$overlay.'/app');$loader->setPsr4('cores\\',$overlay.'/cores');require $root.'/external_stubs.php';
$forbiddenAttempts=0;spl_autoload_register(static function($class)use(&$forbiddenAttempts):void{if(strpos(strtolower($class),'app\\common\\library\\storage\\')===0||strtolower($class)==='app\\common\\library\\upload'){++$forbiddenAttempts;throw new RuntimeException('Storage/upload forbidden in DB-only model test');}},true,true);
$app=new \think\App($root);$app->setEnvName('poc');$app->setRuntimePath($runtime.'/runtime/');$app->initialize();\think\App::$store_id=$tenant;
$actualBuilder=(new ReflectionClass(\app\common\database\DmBuilder::class))->getFileName();
if(realpath($actualBuilder)!==realpath($overlay.'/app/common/database/DmBuilder.php'))throw new RuntimeException('Actual loaded Builder source drift');
$app->config->set(['default'=>'file','stores'=>['file'=>['type'=>'File','path'=>$runtime.'/cache/','prefix'=>'','expire'=>0]]],'cache');$app->config->set(['close'=>true,'default'=>'file','channels'=>['file'=>['type'=>'FileFormat','path'=>$runtime.'/logs/','close'=>true]]],'log');
$config=$app->config->get('database');foreach($config['connections'] as &$conn){$conn['trigger_sql']=true;$conn['debug']=false;}unset($conn);$app->config->set($config,'database');$app->db->setConfig($app->config);
$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=$dm?'YOSHOP_UPLOAD_FILE':'yoshop_upload_file';$query=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;};
$snapshot=static function()use($pdo,$table):array{$stmt=$pdo->query('SELECT * FROM '.$table);$hashes=[];while($row=$stmt->fetch(PDO::FETCH_ASSOC)){$hashes[]=hash('sha256',json_encode($row,JSON_UNESCAPED_UNICODE));}sort($hashes);return ['rows'=>count($hashes),'sha256'=>hash('sha256',implode("\n",$hashes))];};
$before=$snapshot();if((int)$query('SELECT COUNT(*) AS N FROM '.$table.' WHERE STORE_ID=? OR FILE_PATH LIKE ?',[$tenant,$marker.'%'])[0]['n']!==0)throw new RuntimeException('Reserved upload fixture collides');
$triggers=$dm?$query("SELECT TRIGGER_NAME FROM USER_TRIGGERS WHERE TABLE_NAME='YOSHOP_UPLOAD_FILE'"):$query("SHOW TRIGGERS WHERE `Table`='yoshop_upload_file'");$fks=$dm?$query("SELECT CONSTRAINT_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME='YOSHOP_UPLOAD_FILE' AND CONSTRAINT_TYPE='R'"):$query("SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='yoshop_upload_file' AND REFERENCED_TABLE_NAME IS NOT NULL");if($triggers||$fks)throw new RuntimeException('Upload FK/trigger boundary changed');
require __DIR__.'/SqlCoverageRecorder.php';$recorder=new SqlCoverageRecorder($runtime.'/queries.jsonl',$overlay,'PILOT_UPLOAD_ORM_'.$lane.'_'.strtoupper($stage));$app->db->listen(static function($sql,$time=null)use($recorder){if(strpos($sql,'CONNECT:')!==0)$recorder->observe($sql,$time);});
$cases=[];$sourceRows=[];$cleanup=[];$errors=static function(Throwable $e):array{$classes=[];$state=null;$code=null;for($x=$e;$x!==null;$x=$x->getPrevious()){$classes[]=get_class($x);if($x instanceof \think\db\exception\PDOException){$wrapped=$x->getData()['PDO Error Info']??[];$state=$wrapped['SQLSTATE']??$state;$code=$wrapped['Driver Error Code']??$code;}if($x instanceof PDOException&&isset($x->errorInfo)){$state=$x->errorInfo[0]??null;$code=$x->errorInfo[1]??null;}}return ['exception_classes'=>$classes,'sqlstate'=>$state,'native_code'=>$code];};
try{
    foreach(['store','terminal'] as $module){
        $name=$marker.'_'.$stage.'_'.$module.'.jpg';$path=$marker.'/'.$stage.'_'.$module.'-metadata-no-file.jpg';
        if(file_exists($root.'/public/uploads/'.$path)||file_exists($overlay.'/public/uploads/'.$path))throw new RuntimeException('Metadata path unexpectedly exists');
        $data=['storage'=>'local','domain'=>'https://example.invalid/'.$marker.'/domain','file_name'=>$name,'file_path'=>$path,'file_size'=>17,'file_ext'=>'jpg'];
        $class='app\\'.$module.'\\model\\UploadFile';$model=new $class;$returned=null;$error=null;
        try{$returned=$model->add($data,10,0,$tenant);}catch(Throwable $e){$error=$errors($e);}
        $rows=$query('SELECT * FROM '.$table.' WHERE STORE_ID=? AND FILE_NAME=? AND FILE_PATH=?',[$tenant,$name,$path]);$equal=count($rows)===1;
        if($equal){$row=$rows[0];foreach($data as $key=>$value)if(is_int($value)?(string)$row[$key]!== (string)$value:$row[$key]!==$value)$equal=false;
            foreach(['group_id'=>0,'channel'=>$module==='store'?10:20,'file_type'=>10,'uploader_id'=>0,'store_id'=>$tenant,'is_delete'=>0,'is_recycle'=>0] as $key=>$value)if((string)$row[$key]!== (string)$value)$equal=false;
            $raw=$model->getData();$idOwned=isset($raw['file_id'])&&(string)$raw['file_id']===(string)$row['file_id'];$equal=$equal&&$idOwned;
            $sourceRows[$module]=['file_id'=>$row['file_id'],'domain'=>$row['domain'],'storage'=>$row['storage'],'file_name'=>$row['file_name'],'file_path'=>$row['file_path'],'file_size'=>$row['file_size'],'file_type'=>$row['file_type'],'channel'=>$row['channel'],'store_id'=>$row['store_id'],'create_time'=>$row['create_time'],'update_time'=>$row['update_time']];
        }
        $expectedSuccess=!($dm&&$stage==='old');$pass=$expectedSuccess?($returned===true&&$error===null&&$equal):($error!==null&&($error['native_code']??null)==-2007&&count($rows)===0);
        $cases[]=['module'=>$module,'method'=>'UploadFile::add','expected_success'=>$expectedSuccess,'orm_return'=>$returned,'error'=>$error,'rows'=>count($rows),'source_equal'=>$equal,'source_id_owned'=>$equal,'no_actual_file_before'=>true,'pass'=>$pass];
    }
}finally{
    foreach(['store','terminal'] as $module){$name=$marker.'_'.$stage.'_'.$module.'.jpg';$path=$marker.'/'.$stage.'_'.$module.'-metadata-no-file.jpg';$rows=$query('SELECT * FROM '.$table.' WHERE STORE_ID=? AND FILE_NAME=? AND FILE_PATH=?',[$tenant,$name,$path]);$cleaned=0;foreach($rows as $row){$s=$pdo->prepare('DELETE FROM '.$table.' WHERE FILE_ID=? AND STORE_ID=? AND FILE_NAME=? AND FILE_PATH=?');$s->execute([$row['file_id'],$tenant,$name,$path]);$cleaned+=$s->rowCount();}$cleanup[$module]=$cleaned;}
    \think\facade\Cache::clear();$recorder->finish();
}
$audit=$newPdo();$s=$audit->prepare('SELECT COUNT(*) FROM '.$table.' WHERE STORE_ID=? OR FILE_PATH LIKE ?');$s->execute([$tenant,$marker.'%']);$remaining=(int)$s->fetchColumn();$after=$snapshot();$eventRows=[];foreach(file($runtime.'/queries.jsonl',FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)?:[] as $line)$eventRows[]=json_decode($line,true);
$report=['lane'=>$lane,'stage'=>$stage,'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,'cleanup'=>$cleanup,'independent_remaining'=>$remaining,'forbidden_storage_attempts'=>$forbiddenAttempts,'http_requests'=>0,'actual_upload_files_created'=>0,'builder_sha256'=>hash_file('sha256',$overlay.'/app/common/database/DmBuilder.php'),'actual_loaded_builder_sha256'=>hash_file('sha256',$actualBuilder),'model_sha256'=>['store'=>hash_file('sha256',$overlay.'/app/store/model/UploadFile.php'),'terminal'=>hash_file('sha256',$overlay.'/app/terminal/model/UploadFile.php'),'common'=>hash_file('sha256',$overlay.'/app/common/model/UploadFile.php')],'source_fixture_rows'=>$sourceRows,'events'=>$eventRows,'scope'=>'Actual ORM metadata add only; no Upload/StorageDriver/HTTP/file operation'];
file_put_contents($runtime.'/report.json',json_encode($report,JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES));chmod($runtime.'/report.json',0600);
echo json_encode(['lane'=>$lane,'stage'=>$stage,'cases'=>count($cases),'failures'=>$report['failures'],'unchanged'=>$before===$after,'remaining'=>$remaining,'forbidden'=>$forbiddenAttempts,'runtime'=>$runtime]),"\n";exit($report['failures']||$before!==$after||$remaining||$forbiddenAttempts?1:0);
