<?php
declare(strict_types=1);
final class CoreGate {
 public static $total=0;public static $scenario='';public static $ids=[];public static $lease=[];public static $started=0;public static $meta=false;public static $counts=[];public static $last=[];public static $generated=[];public static $orderNo='';
 public static $tables=['AI'=>['store','store_setting','upload_file','user','user_oauth','user_subsidy','account_settlement','user_balance_log'],'ZHCT'=>['user','staff','restaurants','prepay_order','meal_order','staff_department','department','config','one_card_account_map','consume_limit','consume_limit_department','consume_limit_scenario']];
 public static function rec($r){$r['scenario']=self::$scenario;event('business-native.jsonl',$r);}
 public static function kind($db,$sql){$plain=preg_replace("/'(?:''|\\\\.|[^'\\\\])*'/s",'?', $sql);ck(!preg_match('/;|--|\/\*|\*\/|#|\b(?:DELETE|REPLACE|MERGE|CREATE|ALTER|DROP|TRUNCATE|GRANT|REVOKE|OUTFILE|DUMPFILE|LOAD|EXEC|EXECUTE|SLEEP|BENCHMARK|UTL_HTTP)\b/i',$plain),'Forbidden business SQL');if(preg_match('/^\s*SELECT\b/i',$sql)){ck(!preg_match('/\bINTO\b/i',$plain),'SELECT INTO forbidden');preg_match_all('/\b(yoshop|ydy)_([a-z0-9_]+)/i',$sql,$m,PREG_SET_ORDER);foreach($m as $r)ck(($db==='AI'?strtolower($r[1])==='yoshop':strtolower($r[1])==='ydy')&&in_array(strtolower($r[2]),self::$tables[$db],true),'Unlisted source SELECT '.$r[0]);return 'SELECT';}if(preg_match('/^SHOW FULL COLUMNS FROM `((yoshop|ydy)_([a-z_]+))`$/D',$sql,$m)){ck(($db==='AI'?strtolower($m[2])==='yoshop':strtolower($m[2])==='ydy')&&in_array($m[3],self::$tables[$db],true),'Exact SHOW scope');return 'metadata_SHOW';}if($sql==='CALL SP_TABLEDEF(?, ?)'&&self::$meta)return 'metadata_CALL';if(preg_match('/^(INSERT INTO|UPDATE)\s+[`"]?((?:yoshop|ydy)_[a-z_]+)[`"]?/i',$sql,$m)){ck(in_array(self::$scenario,['DIRECT_002'],true),'No DML in this case');return strtoupper($m[1])==='UPDATE'?'UPDATE':'INSERT';}throw new RuntimeException('Unknown original SQL');}
 public static function vals($sql,$bind){$s=preg_replace('/[`"]([a-zA-Z_][a-zA-Z0-9_]*)[`"]/', '$1', $sql);$pairs=[];if(preg_match('/^INSERT INTO (\w+) SET (.+)$/is',$s,$m)){$table=strtolower($m[1]);foreach(explode(',',$m[2]) as $p){ck(preg_match('/^\s*(\w+)\s*=\s*(:\w+)\s*$/D',$p,$a)===1,'Bound single INSERT SET');$pairs[]=[$a[1],$a[2]];}}elseif(preg_match('/^INSERT INTO (\w+)\s*\(([^)]+)\)\s*VALUES\s*\(([^)]+)\)\s*$/is',$s,$m)){$table=strtolower($m[1]);$ks=explode(',',$m[2]);$vs=explode(',',$m[3]);ck(count($ks)===count($vs),'Bound INSERT lengths');foreach($ks as $i=>$k)$pairs[]=[trim($k),trim($vs[$i])];}else throw new RuntimeException('Only single bound INSERT');$out=[];foreach($pairs as [$k,$token]){ck(preg_match('/^:\w+$/D',$token)===1&&!array_key_exists($k,$out),'Only named native bind');$b=$bind[ltrim($token,':')]??null;ck(is_array($b)&&count($b)===2,'Original bind plan');$out[strtolower($k)]=$b[0];}return [$table,$out];}
 public static function equalFields($actual,$expected){$ak=array_keys($actual);$ek=array_keys($expected);sort($ak);sort($ek);ck($ak===$ek,'Exact original write field set');foreach($expected as $k=>$v){$a=$actual[$k];if($v==='@time'){if(is_numeric($a)&&ctype_digit((string)$a))$t=(int)$a;else{$t=strtotime((string)$a);ck(is_string($a)&&preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/D',$a)===1,'Original exact DATETIME bind');}ck($t>=self::$started-2&&$t<=time()+2,'Current timestamp bind');}elseif($v==='@ormtime'){ck(is_string($a)&&preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{6}$/D',$a)===1,'Original ORM microsecond string bind');$d=DateTimeImmutable::createFromFormat('!Y-m-d H:i:s.u',$a);ck($d!==false&&$d->format('Y-m-d H:i:s.u')===$a&&$d->getTimestamp()>=self::$started-2&&$d->getTimestamp()<=time()+2,'Original ORM valid current time');}elseif($v==='@no'){ck(is_string($a)&&preg_match('/^[0-9]{12,32}$/D',$a)===1,'Original orderNo bytes');self::$orderNo=$a;}elseif($v==='@directNo')ck($a===self::$orderNo,'Original generated orderNo reference');else ck((string)$a===(string)$v,'Exact bound write value '.$k);}}
 public static function bindTypes($db,$sql,$bind){global $lane,$runtime;$clean=preg_replace('/[`"]([a-zA-Z_][a-zA-Z0-9_]*)[`"]/', '$1', $sql);ck(preg_match('/^(?:INSERT INTO|UPDATE)\s+((?:yoshop|ydy)_\w+)/i',$clean,$m)===1,'DML table type scope');$table=strtolower($m[1]);$pre=json_decode(file_get_contents($runtime.'/preflight-'.$lane.'.json'),true);$short=substr($table,$db==='AI'?7:4);$cols=[];foreach($pre['schema'][$db][$short]['columns'] as $c)$cols[strtolower($c['column_name'])]=strtolower($c['data_type']);$pairs=[];if(stripos($clean,'INSERT INTO')===0&&stripos($clean,' SET ')===false){ck(preg_match('/\(([^)]+)\)\s*VALUES\s*\(([^)]+)\)/i',$clean,$v)===1,'Type column/token pairs');$ks=explode(',',$v[1]);$vs=explode(',',$v[2]);foreach($ks as $i=>$k)$pairs[]=[trim($k),ltrim(trim($vs[$i]),':')];}else{preg_match_all('/(?:\w+\.)?(\w+)\s*=\s*:(\w+)/',$clean,$v,PREG_SET_ORDER);foreach($v as $p)$pairs[]=[$p[1],$p[2]];}$qualifiedIncScope=[];if(preg_match('/^UPDATE\s+yoshop_user\s+SET\s+pay_money\s*=\s*pay_money\s*\+\s*0\.02\s+WHERE\s+yoshop_user\.store_id\s*=\s*:(\w+)\s+AND\s+user_id\s*=\s*:(\w+)\s*$/i',$clean,$q))$qualifiedIncScope=[$q[1]];foreach($pairs as [$field,$token]){$field=strtolower($field);ck(isset($cols[$field])&&isset($bind[$token]),'Actual metadata column/binding');$type=$cols[$field];$expected=preg_match('/int|bool|bit/',$type)?PDO::PARAM_INT:(preg_match('/decimal|numeric|number|float|double|real/',$type)?21:PDO::PARAM_STR);if($field==='store_id'&&in_array($token,$qualifiedIncScope,true)){$expected=PDO::PARAM_STR;ck($bind[$token][0]===2147001111,'Exact original qualified scope tenant/native int');}ck(is_array($bind[$token])&&$bind[$token][1]===$expected,'Exact original schema bind type '.$field);}}
 public static function plan($db,$sql,$bind){$kind=self::kind($db,$sql);if(in_array($kind,['INSERT','UPDATE'],true)){global $runtime;$shape=static function($s){$s=preg_replace('/[`\"]([a-zA-Z_][a-zA-Z0-9_]*)[`\"]/','$1',$s);$s=preg_replace('/:\w+/',':B',$s);return trim(preg_replace('/\s+/',' ',$s));};$plans=json_decode(file_get_contents($runtime.'/exact-SQL-bind-plan.json'),true)['entries'];$matches=array_values(array_filter($plans,static function($p)use($db,$sql,$shape){return $p['connection']===$db&&$shape($p['queryString'])===$shape($sql);}));ck(count($matches)===1,'Exact original DML SQL shape/field order/predicates');self::bindTypes($db,$sql,$bind);}$values=[];foreach($bind as $k=>$b)$values[$k]=is_array($b)?$b[0]:$b;
  if($kind==='SELECT'){if(preg_match('/\bUSER_(?:TAB_COLUMNS|CONSTRAINTS|CONS_COLUMNS)\b/i',$sql)){ck(isset($values[0])&&in_array(strtolower((string)$values[0]),array_map(static function($t)use($db){return ($db==='AI'?'yoshop_':'ydy_').$t;},self::$tables[$db]),true),'Exact source metadata binding');return;}$flat=array_map('strval',array_values($values));if(stripos($sql,'ydy_config')!==false)ck(count(array_intersect($flat,['one_card','account_deduction_order']))===1,'Exact two runtime config keys');if(stripos($sql,'yoshop_user')!==false&&stripos($sql,'yoshop_user_subsidy')===false&&stripos($sql,'yoshop_user_balance_log')===false&&stripos($sql,'yoshop_user_oauth')===false)ck(count(array_intersect($flat,array_map('strval',[self::$ids['AI_U1'],self::$ids['AI_U2']])))>0,'Own AI identity SELECT');return;}
  if($kind==='metadata_CALL'){ck(count($values)===2&&$values[0]===($db==='AI'?'AIZHCT_APP':'ZHCT_APP')&&in_array(strtolower($values[1]),array_map(static function($t)use($db){return ($db==='AI'?'yoshop_':'ydy_').$t;},self::$tables[$db]),true),'Exact metadata CALL scope');return;}
  if($kind!=='INSERT'&&$kind!=='UPDATE')return;
  $ids=self::$ids;$u=$ids['AI_U1'];$su=$ids['STORE_U1'];$r=$ids['STORE_R1'];$isPre=in_array(self::$scenario,['PREPAY_FIRST','PREPAY_FIRST_RETRY','PREPAY_FIRST_RETRY1'],true);
  if($kind==='INSERT'){[$table,$v]=self::vals($sql,$bind);$limit=$isPre?['ydy_meal_order'=>1]:['ydy_meal_order'=>1,'yoshop_user_balance_log'=>1,'yoshop_account_settlement'=>1];ck(isset($limit[$table])&&($db==='AI')===(strpos($table,'yoshop_')===0)&&empty(self::$counts['INSERT.'.$table]),'Exact INSERT table/count');
   if($table==='ydy_meal_order'){$expected=['order_no'=>'@no','total_price'=>$isPre?'0.02':0.02,'ori_total_price'=>$isPre?'0.02':0.02,'order_price'=>$isPre?'0.02':0.02,'pay_price'=>$isPre?'0.02':0.02,'pay_method'=>'balance','pay_status'=>$isPre?10:20,'pay_time'=>$isPre?'2000-01-01 00:00:00':'@time','meal_date'=>date('Y-m-d'),'meal_times'=>$isPre?((int)date('H')<10?1:((int)date('H')<16?2:3)):1,'meal_number'=>$isPre?1:0,'bind_id'=>0,'bind_time'=>'@time','equipment_code'=>'','last_meal_time'=>'@time','source'=>$isPre?8:4,'user_id'=>$su,'restaurant_id'=>$r,'user_mobile'=>'00000001111','user_name'=>'CDXCORE11S1_U1','restaurant_method'=>1,'remark'=>$isPre?'CDXCORE11S1_COFFEE':'CDXCORE11S1_DIRECT_002','order_status'=>$isPre?10:30,'create_time'=>$isPre?'@time':'@ormtime','update_time'=>$isPre?'@time':'@ormtime'];if(!$isPre)$expected=array_merge($expected,['paycode'=>'','card_id'=>'','package_fee'=>0,'print_status'=>0,'origin_department_uuid'=>'','origin_department_name'=>'','management_fee'=>0,'discount_amount'=>0,'marketing_strategy_id'=>0,'marketing_strategy_time_range_id'=>0,'time_range_mode'=>0,'strategy_time_range_text'=>'','actual_strategy_type'=>0,'actual_strategy_value'=>'0.00']);self::equalFields($v,$expected);}
   elseif($table==='yoshop_user_balance_log'){ck(isset(self::$generated['ydy_meal_order']),'Actual generated direct order');self::equalFields($v,['scene'=>20,'describe'=>'用户消费：'.self::$orderNo,'store_id'=>2147001111,'order_id'=>self::$generated['ydy_meal_order'],'user_id'=>$u,'money'=>-0.02,'balance_after'=>'0.97','create_time'=>'@time']);}
   else self::equalFields($v,['settlement_no'=>'CS:'.self::$orderNo,'biz_type'=>'consume','biz_order_no'=>self::$orderNo,'refund_no'=>'','user_id'=>$u,'amount'=>'0.02','subsidy_amount'=>'0.00','cash_amount'=>'0.02','gift_amount'=>'0.00','deduction_order_snapshot'=>'["gift","subsidy","cash"]','status'=>20,'error_message'=>'','store_id'=>2147001111]);
   self::$last[$db]=$table;return;}
  $clean=preg_replace('/[`"]([a-zA-Z_][a-zA-Z0-9_]*)[`"]/', '$1', $sql);ck(preg_match('/^UPDATE\s+(\w+)\s+SET\s+(.+?)\s+WHERE\s+(.+)$/is',$clean,$m)===1,'Exact UPDATE syntax');$table=strtolower($m[1]);$set=[];foreach(explode(',',$m[2]) as $p){ck(preg_match('/^\s*(\w+)\s*=\s*(.+?)\s*$/D',$p,$a)===1,'Exact UPDATE assignment');$token=$a[2];if(preg_match('/^:\w+$/D',$token))$set[strtolower($a[1])]=$values[ltrim($token,':')];else{ck(strtolower($a[1])==='pay_money'&&preg_match('/^pay_money\s*\+\s*0\.02$/i',$token)===1,'Only original increment 0.02');$set['pay_money']='@inc0.02';}}
  $where=[];ck(!preg_match('/\b(?:OR|IN|EXISTS|SELECT)\b/i',$m[3]),'Single own UPDATE predicate');preg_match_all('/(?:\w+\.)?(\w+)\s*=\s*(:\w+|\d+)(?=\s|\)|$)/',$m[3],$matches,PREG_SET_ORDER);foreach($matches as $a){$where[strtolower($a[1])]=$a[2][0]===':'?$values[ltrim($a[2],':')]:$a[2];}
  if($isPre){ck($db==='ZHCT'&&$table==='ydy_prepay_order'&&empty(self::$counts['UPDATE.'.$table]),'Single own prepay CAS');self::equalFields($where,['id'=>$ids['STORE_P1'],'meal_order_id'=>0]);self::equalFields($set,['meal_order_id'=>self::$generated['ydy_meal_order'],'meal_order_no'=>self::$orderNo,'user_id'=>$su,'update_by'=>'miniapp']);}
  elseif($table==='ydy_meal_order'){ck($db==='ZHCT'&&empty(self::$counts['UPDATE.'.$table]),'Single own order UPDATE');self::equalFields($where,['id'=>self::$generated['ydy_meal_order']]);self::equalFields($set,['cash'=>0.02,'subsidy'=>0,'gift'=>0,'remain_balance'=>0.97,'remain_cash'=>0.97,'remain_subsidy'=>0,'remain_gift'=>0,'deduction_order_snapshot'=>'["gift","subsidy","cash"]','account_settlement_id'=>self::$generated['yoshop_account_settlement'],'update_time'=>'@ormtime']);}
  elseif($table==='yoshop_user'){ck($db==='AI'&&(self::$counts['UPDATE.'.$table]??0)<2,'Two own wallet updates only');if(isset($set['balance'])){self::equalFields($where,['user_id'=>$u,'store_id'=>2147001111,'is_delete'=>0]);self::equalFields($set,['balance'=>'0.97','gift_balance'=>'0.00']);}else{self::equalFields($where,['user_id'=>$u,'store_id'=>2147001111]);self::equalFields($set,['pay_money'=>'@inc0.02']);}}
  else throw new RuntimeException('Unapproved UPDATE table');self::$last[$db]=$table;
 }
}
class CoreStatement extends PDOStatement {private $bound=[];private $db='';protected function __construct($db){$this->db=$db;}public function bindValue($p,$v,$type=PDO::PARAM_STR){$ok=parent::bindValue($p,$v,$type);$this->bound[(string)$p]=['value'=>$v,'pdo_type'=>$type,'php_type'=>gettype($v),'ok'=>$ok];return $ok;}public function bindParam($p,&$v,$type=PDO::PARAM_STR,$len=0,$opts=null){throw new RuntimeException('bindParam not authorized');}public function execute($params=null){$kind=CoreGate::kind($this->db,$this->queryString);ck(++CoreGate::$total<=(getenv('CORE_LANE')==='B'?80:160),'Exact business native SQL cap');$r=['connection'=>$this->db,'layer'=>'actual PDOStatement.execute','queryString'=>$this->queryString,'bindValue'=>$this->bound,'execute_array'=>$params,'operation'=>$kind];try{$ok=parent::execute($params);$r['success']=$ok;$r['rowCount']=$this->rowCount();CoreGate::rec($r);if(in_array($kind,['INSERT','UPDATE'],true)){ck($this->rowCount()===1,'Business single-row affected1');$table=CoreGate::$last[$this->db];CoreGate::$counts[$kind.'.'.$table]=(CoreGate::$counts[$kind.'.'.$table]??0)+1;}return $ok;}catch(Throwable $e){$r['error']=['class'=>get_class($e),'message'=>$e->getMessage()];CoreGate::rec($r);throw $e;}}}
class CorePDO extends PDO {public $db;public function prepare($sql,$options=[]){$kind=CoreGate::kind($this->db,$sql);try{$s=parent::prepare($sql,$options);CoreGate::rec(['connection'=>$this->db,'layer'=>'actual PDO.prepare','queryString'=>$sql,'operation'=>$kind,'success'=>$s!==false]);return $s;}catch(Throwable $e){CoreGate::rec(['connection'=>$this->db,'layer'=>'actual PDO.prepare','queryString'=>$sql,'error'=>['class'=>get_class($e),'message'=>$e->getMessage()]]);throw $e;}}public function query($sql,...$args){$s=$this->prepare($sql);$s->execute();if($args)$s->setFetchMode(...$args);return $s;}public function exec($sql){ck(in_array(CoreGate::$scenario,['DIRECT_002'],true)&&preg_match('/^(?:SAVEPOINT trans[2-9]|ROLLBACK TO SAVEPOINT trans[2-9])$/D',$sql),'Original savepoint only');$n=parent::exec($sql);CoreGate::rec(['connection'=>$this->db,'layer'=>'actual PDO.exec transaction','queryString'=>$sql,'rowCount'=>$n]);return $n;}public function beginTransaction(){ck(in_array(CoreGate::$scenario,['DIRECT_002'],true),'No transaction outside approved writes');$ok=parent::beginTransaction();CoreGate::rec(['connection'=>$this->db,'layer'=>'actual PDO.beginTransaction','success'=>$ok]);return $ok;}public function commit(){$ok=parent::commit();CoreGate::rec(['connection'=>$this->db,'layer'=>'actual PDO.commit','success'=>$ok]);return $ok;}public function rollBack(){$ok=parent::rollBack();CoreGate::rec(['connection'=>$this->db,'layer'=>'actual PDO.rollBack','success'=>$ok]);return $ok;}public function lastInsertId($name=null){$id=parent::lastInsertId($name);$table=CoreGate::$last[$this->db]??'';CoreGate::rec(['connection'=>$this->db,'layer'=>'actual PDO.lastInsertId','table'=>$table,'value'=>$id,'php_type'=>gettype($id)]);ck(ctype_digit((string)$id)&&(int)$id>0,'Actual generated identity');CoreGate::$generated[$table]=(int)$id;return $id;}}
trait CoreConnection {protected function createPdo($dsn,$username,$password,$params){$db=$this->getConfig('prefix')==='yoshop_'?'AI':'ZHCT';$dm=getenv('POC_DIALECT')==='dm';$expected=$dm?'dm:host=127.0.0.1;port='.getenv('POC_'.$db.'_PORT').';schema='.getenv('POC_'.$db.'_SCHEMA'):'mysql:host='.getenv('POC_'.$db.'_HOST').';port='.getenv('POC_'.$db.'_PORT').';dbname='.getenv('POC_'.$db.'_SCHEMA').';charset='.getenv('POC_'.$db.'_CHARSET');ck($dsn===$expected&&$username===getenv('POC_'.$db.'_USER')&&$password===getenv('POC_'.$db.'_PASSWORD')&&!isset($params[1002]),'Exact approved two native connections');$p=new CorePDO($dsn,$username,$password,$params);$p->db=$db;ck($p->setAttribute(PDO::ATTR_STATEMENT_CLASS,[CoreStatement::class,[$db]]),'Native statement capture');return $p;}public function getPDOStatement(string $sql,array $bind=[],bool $master=false,bool $procedure=false):PDOStatement{$db=$this->getConfig('prefix')==='yoshop_'?'AI':'ZHCT';CoreGate::$meta=$sql==='CALL SP_TABLEDEF(?, ?)';try{CoreGate::plan($db,$sql,$bind);CoreGate::rec(['connection'=>$db,'layer'=>'original framework bind plan','queryString'=>$sql,'bind_plan'=>$bind,'procedure'=>$procedure]);return parent::getPDOStatement($sql,$bind,$master,$procedure);}catch(Throwable $e){CoreGate::rec(['connection'=>$db,'layer'=>'intercepted original plan failure','queryString'=>$sql,'bind_plan'=>$bind,'error'=>['class'=>get_class($e),'message'=>$e->getMessage()]]);throw $e;}finally{CoreGate::$meta=false;}}}
class CoreMysql extends \think\db\connector\Mysql {use CoreConnection;public function getBuilderClass():string{return \think\db\builder\Mysql::class;}}
class CoreDm extends \app\common\database\Dm {use CoreConnection;}
