<?php
declare(strict_types=1);
$base='/workspace/front-db-rehearsal-20261023';$cfg=json_decode(file_get_contents($base.'/private/float-fixture.json'),true);
if($cfg['user']!=='FRONT24_FLOAT'||$cfg['port']!==15236)throw new RuntimeException('fixture target');
$pdo=new PDO('dm:host=127.0.0.1;port=15236;schema=FRONT24_FLOAT',$cfg['user'],$cfg['password'],[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);
if($pdo->query("SELECT PARA_VALUE FROM V\$DM_INI WHERE PARA_NAME='INSTANCE_NAME'")->fetchColumn()!=='FS1R20261021')throw new RuntimeException('instance');
define('APP_PATH',$base.'/source/application/');define('ROOT_PATH',$base.'/float-runtime/');define('RUNTIME_PATH',ROOT_PATH.'runtime/');define('VENDOR_PATH',$base.'/source/vendor/');define('BUSINESS','zhct_zywxb');define('TP_ENV','prod');
require $base.'/source/thinkphp/base.php';\think\Loader::addNamespace('app',APP_PATH);require $base.'/source/thinkphp/helper.php';require APP_PATH.'common.php';
\think\Config::set('database',['type'=>\app\cores\database\Dm::class,'hostname'=>'127.0.0.1','hostport'=>15236,'database'=>'FRONT24_FLOAT','username'=>$cfg['user'],'password'=>$cfg['password'],'prefix'=>'ydy_','debug'=>false]);
\think\Config::set('log',['type'=>'File','path'=>ROOT_PATH.'log/']);\think\Config::set('cache',['type'=>'File','path'=>ROOT_PATH.'cache/']);
$db=\think\Db::connect();$source=json_decode(file_get_contents($base.'/front-four-fixed-float-source-oracles.json'),true);$results=[];
foreach($source['cases']as$c){$v=$c['input'];
foreach(['food_weight','ssb','saccharides','price']as$f)foreach(['insert','update','insertAll']as$op){
 $table=$f==='food_weight'?'dish':'dishes';$model=$f==='food_weight'?new \app\model\Dish():new \app\model\Cater();$db->startTrans();
 try{
  if($op==='insert'){$id=$model->add(['uuid'=>'synthetic-new',$f=>$v,'weight'=>'12.3456']);if((int)$id<=1)throw new RuntimeException('PDO generated ID');$row=$model->readById($id);}
  elseif($op==='insertAll'){$model->addAll([['uuid'=>'synthetic-bulk-a',$f=>$v,'weight'=>'12.3456'],['uuid'=>'synthetic-bulk-b',$f=>$v,'weight'=>'12.3456']]);$rows=$model->whereIn('uuid',['synthetic-bulk-a','synthetic-bulk-b'])->select()->toArray();if(count($rows)!==2)throw new RuntimeException('bulk rows');$row=$rows[0];foreach($rows as$r)if(bin2hex(pack('G',(float)$r[$f]))!==$c['expected'][$f]['ieee32_hex'])throw new RuntimeException('bulk value');}
  else{if($f==='price'){$ref=new ReflectionClass(\app\p\logic\Cater::class);$logic=$ref->newInstanceWithoutConstructor();$prop=$ref->getProperty('CaterModel');$prop->setAccessible(true);$prop->setValue($logic,$model);$res=$logic->updatePrice('fixture',$v);if($res['code']!==0)throw new RuntimeException('actual price logic');}else{$model->modifyById(1,[$f=>$v,'weight'=>'12.3456']);}$row=$model->readById(1);}
  $ok=bin2hex(pack('G',(float)$row[$f]))===$c['expected'][$f]['ieee32_hex'];if(!$ok)throw new RuntimeException('source float bits differ');
  if(!($op==='update'&&$f==='price')&&(string)$row['weight']!=='12.3456')throw new RuntimeException('unrelated decimal changed');
  $expectedLogical=(float)number_format((float)$c['expected'][$f]['physical_decimal'],2,'.','');
  if($f!=='food_weight'){$ref=new ReflectionClass(\app\p\logic\Cater::class);$logic=$ref->newInstanceWithoutConstructor();$prop=$ref->getProperty('CaterModel');$prop->setAccessible(true);$prop->setValue($logic,$model);$dto=$logic->findCaterName((int)$row['id']);if($dto[$f]!==$expectedLogical)throw new RuntimeException('actual dish DTO differs from MySQL');}
  else{$dto=$model->readDish('fixture');$matches=array_values(array_filter($dto['data'],function($r)use($row){return (string)$r['id']===(string)$row['id'];}));if(count($matches)!==1||$matches[0][$f]!==$expectedLogical)throw new RuntimeException('actual component DTO differs from MySQL');}
  if($f==='price'&&$op==='update'){$menu=(new \app\model\Bill())->bill_list('2026-10-24',1);if(count($menu)!==1||$menu[0]['price']!==$expectedLogical)throw new RuntimeException('actual menu DTO differs from MySQL');}
  $results[]=['case'=>$c['case_id'],'field'=>$f,'operation'=>$op,'passed'=>true,'read_dto_matches_mysql'=>true];
 }catch(Throwable$e){$results[]=['case'=>$c['case_id'],'field'=>$f,'operation'=>$op,'passed'=>false,'error'=>get_class($e).':'.$e->getMessage()];}
 finally{$db->rollback();}
 if((int)$model->count()!==1||(float)$model->readById(1)[$f]!==0.0)throw new RuntimeException('fixture rollback failed');
}}
$fail=array_values(array_filter($results,function($x){return !$x['passed'];}));$r=['checks'=>count($results),'failed'=>count($fail),'failures'=>$fail,'synthetic_only'=>true,'business_rows_mutated'=>false,'models'=>['Dish','Cater'],'real_logic'=>'Cater::updatePrice','all_cases'=>$results];
file_put_contents($base.'/private/real-float-all-inputs-result.json',json_encode($r,JSON_PRETTY_PRINT));echo json_encode(['checks'=>count($results),'failed'=>count($fail),'first_failures'=>array_slice($fail,0,4)]),PHP_EOL;exit($fail?1:0);
