<?php
declare(strict_types=1);
require_once __DIR__.'/NativeContract.php';require_once __DIR__.'/Budget.php';
class NFGate {
 public static $journal=[],$case=[],$dml=0,$lane='',$pdo,$warningPending=false,$warningReads=0,$dry=false;
 public static $tables=['ydy_nutrition','ydy_nutrition_template','ydy_nutrition_template_ext','ydy_nutrition_set','ydy_nutrition_category','ydy_nutrition_time','ydy_user','ydy_config','ydy_identity_type'];
 public static function sql($sql){
  if($sql==='SHOW WARNINGS'){if(self::$lane!=='B'||!self::$warningPending||self::$warningReads!==0)throw new RuntimeException('Only immediate source warnings');return 'warnings';}
  if(preg_match('/^CALL SP_TABLEDEF\(\?, \?\)$/D',$sql))return 'metadata';
  if(preg_match('/^SHOW (?:FULL )?COLUMNS FROM \x60?(ydy_[a-z_]+)\x60?$/iD',$sql,$m)&&in_array(strtolower($m[1]),self::$tables,true))return 'metadata';
  $plain=preg_replace("/'(?:''|[^'])*'/s","''",$sql);
  if(preg_match('/;|--|\/\*|\b(MERGE|DROP|ALTER|CREATE|TRUNCATE|CALL|EXEC|OUTFILE|SLEEP|BENCHMARK|DBLINK)\b/i',$plain))throw new RuntimeException('Unsafe SQL token');
  if(preg_match('/^\s*SELECT\b/i',$sql)){$names=[];preg_match_all('/\b(?:FROM|JOIN)\s+([\x60"]?[a-z_][a-z_0-9]*[\x60"]?(?:\.[\x60"]?[a-z_][a-z_0-9]*[\x60"]?)?)/i',$plain,$names);$allowed=array_merge(self::$tables,['user_tab_columns','user_constraints','user_cons_columns','user_objects']);foreach($names[1]as $name)if(!in_array(strtolower(str_replace(["\x60",'"'],'',$name)),$allowed,true))throw new RuntimeException('Unfrozen SELECT table');return 'select';}
  if((self::$case['path']??'')!=='/m/nutrition/update')throw new RuntimeException('Non-update cannot write');
  if($sql!==PlainScalarNativeContract::expectedSql(self::$lane,self::$case['spec']))throw new RuntimeException('Exact native UPDATE bytes');return 'update';
 }
 public static function binds($sql,$binds,$params=null){
  if($params!==null)throw new RuntimeException('No implicit execute params');$op=self::sql($sql);
  if($op==='warnings'&&$binds!==[])throw new RuntimeException('Warnings zero binds');
  if($op==='metadata'&&strpos($sql,'CALL ')===0){$v=array_column(array_values($binds),'value');if(count($v)!==2||$v[0]!=='ZHCT_APP'||!in_array(strtolower((string)$v[1]),self::$tables,true))throw new RuntimeException('Metadata CALL scope');}
  if($op==='update'){
   if(self::$dml>=1)throw new RuntimeException('One target UPDATE per request');
   $own=['B'=>'B8247519-0EDA-6239-9A8A-FEA2D6C86F4B','C'=>'FCEECEFB-C32E-4977-8A65-EE2210A42095'];
   PlainScalarNativeContract::assertUpdate($sql,$binds,$params,self::$lane,self::$case['spec'],self::$case['params'],$own[self::$lane]);
   if(!self::$dry)nfBudgetTake('target-update',3);self::$dml++;
  }
  if(!self::$dry)nfBudgetTake('native',self::$lane==='B'?43:75);return $op;
 }
 public static function warnings(): void {
  if(self::$lane!=='B')return;self::$warningPending=true;
  try{$s=self::$pdo->query('SHOW WARNINGS');$rows=$s->fetchAll(PDO::FETCH_ASSOC);self::$journal[]=['layer'=>'source.SHOW_WARNINGS','sql'=>'SHOW WARNINGS','binds'=>[],'rows'=>$rows,'connection_id'=>spl_object_hash(self::$pdo),'errorInfo'=>$s->errorInfo(),'success'=>true];}
  catch(Throwable $e){self::$journal[]=['layer'=>'source.SHOW_WARNINGS','sql'=>'SHOW WARNINGS','success'=>false,'tool_error'=>['class'=>get_class($e),'message'=>$e->getMessage()]];}
  finally{self::$warningPending=false;self::$warningReads++;}
 }
}
class NFStatement extends PDOStatement {
 private $bound=[];protected function __construct(){}
 public function bindValue($p,$v,$t=PDO::PARAM_STR){$ok=parent::bindValue($p,$v,$t);$this->bound[(string)$p]=['value'=>$v,'type'=>$t,'php_type'=>gettype($v),'ok'=>$ok];return $ok;}
 public function execute($params=null){$r=['layer'=>'PDOStatement.execute','sql'=>$this->queryString,'binds'=>$this->bound,'params'=>$params,'success'=>false];$op=null;$error=null;$ok=false;try{$op=NFGate::binds($this->queryString,$this->bound,$params);$ok=parent::execute($params);$r['success']=$ok;$r['rowCount']=$this->rowCount();$r['errorInfo']=$this->errorInfo();}
 catch(Throwable $e){$error=$e;$r['error']=['class'=>get_class($e),'message'=>$e->getMessage(),'errorInfo'=>$this->errorInfo()];}
 finally{NFGate::$journal[]=$r;if($op==='update')NFGate::warnings();}
 if($error)throw $error;return $ok;
 }
}
class NFPdo extends PDO {
 public function prepare($sql,$options=[]){$r=['layer'=>'PDO.prepare','sql'=>$sql,'success'=>false];try{NFGate::sql($sql);$s=parent::prepare($sql,$options);$r['success']=$s!==false;return $s;}catch(Throwable $e){$r['error']=['class'=>get_class($e),'message'=>$e->getMessage()];throw $e;}finally{NFGate::$journal[]=$r;}}
 public function exec($sql){throw new RuntimeException('Unfrozen PDO.exec');}
 public function query($sql,...$args){NFGate::binds($sql,[]);$s=parent::query($sql,...$args);NFGate::$journal[]=['layer'=>'PDO.query','sql'=>$sql,'success'=>$s!==false];return $s;}
 public function beginTransaction(){throw new RuntimeException('No new business transaction shape');}
 public function commit(){throw new RuntimeException('No new business commit shape');}
 public function rollBack(){throw new RuntimeException('No new business rollback shape');}
}
