<?php
declare(strict_types=1);
// SELECT-only integration of the experimental Statement via per-connection params.
require __DIR__.'/ExactStatement.php';
$phase='preflight';$assertions=0;
function exactCheck($expected,$actual): void {
    global $assertions,$phase;
    ++$assertions;
    if($expected!==$actual){throw new RuntimeException('Contract mismatch');}
    if(getenv('EXACT_FETCH_TRACE')==='1'){fwrite(STDERR,'EXACT_FETCH_PASS '.$phase.' '.$assertions.PHP_EOL);}
}
try {
    $framework=(string)getenv('POC_FRAMEWORK');
    $root=realpath((string)getenv('POC_APP_ROOT'));
    if(!in_array($framework,['store','ai_api'],true)||!$root||strpos($root,'/tmp/zhct-')!==0||!defined('PDO::EXTRA_DECIMAL')){throw new RuntimeException('Isolated candidate runtime required');}
    $prefix=$framework==='store'?'POC_':'POC_AI_';
    foreach(['HOST','PORT','SCHEMA','USER','PASSWORD'] as $key){if((string)getenv($prefix.$key)===''){throw new RuntimeException('Approved environment required');}}
    $phase='bootstrap';
    if($framework==='store'){
        define('APP_PATH',$root.'/application/');
        require $root.'/thinkphp/base.php';
        require $root.'/application/cores/database/Dm.php';
        $class=\app\cores\database\Dm::class;
    }else{
        require $root.'/vendor/autoload.php';
        $class=\app\common\database\Dm::class;
    }
    $config=['type'=>$class,'hostname'=>getenv($prefix.'HOST'),'hostport'=>(int)getenv($prefix.'PORT'),'database'=>getenv($prefix.'SCHEMA'),'username'=>getenv($prefix.'USER'),'password'=>getenv($prefix.'PASSWORD'),'prefix'=>'','debug'=>false,'params'=>[
        PDO::ATTR_CASE=>PDO::CASE_LOWER,PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_STRINGIFY_FETCHES=>true,PDO::EXTRA_DECIMAL=>1,
        PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC,
        PDO::ATTR_STATEMENT_CLASS=>[ExactStatement::class,[]],
        PDO::ATTR_EMULATE_PREPARES=>false,
    ]];
    if($framework==='store'){$db=\think\Db::connect($config,true);}else{$manager=new \think\DbManager();$manager->setConfig(['default'=>'probe','connections'=>['probe'=>$config]]);$db=$manager->connect('probe');}
    $sql="SELECT 7 AS I,CAST('9007199254740993.01' AS DECIMAL(30,2)) AS D,CAST('0.00' AS DECIMAL(5,2)) AS Z FROM DUAL";
    $expected=['i'=>7,'d'=>'9007199254740993.01','z'=>'0.00'];
    $phase='query';exactCheck([$expected],$db->query($sql));
    $table='('.$sql.') EXACT_Q';
    // TP6 intentionally skips find without WHERE/ORDER; this is not a fetch failure.
    $phase='find';exactCheck($expected,$db->table($table)->where('i',7)->find());
    $phase='select';$rows=$db->table($table)->select();if(is_object($rows)){$rows=$rows->toArray();}exactCheck([$expected],$rows);
    $phase='value';exactCheck('9007199254740993.01',$db->table($table)->value('d'));
    $phase='column';exactCheck(['9007199254740993.01'],$db->table($table)->column('d'));
    $phase='column-key';exactCheck([7=>'9007199254740993.01'],$db->table($table)->column('d','i'));
    $phase='count';exactCheck(1,$db->table($table)->count());
    $phase='empty-count';exactCheck(0,$db->table($table)->where('i',-1)->count());
    $phase='missing-value';exactCheck('fallback',$db->table($table)->where('i',-1)->value('d','fallback'));
    $phase='bound-query';exactCheck([['d'=>'9007199254740993.01']],$db->query('SELECT CAST(? AS DECIMAL(30,2)) AS D FROM DUAL',['9007199254740993.01']));
    if($framework==='ai_api'){$phase='cursor';$rows=[];foreach($db->table($table)->cursor() as $row){$rows[]=$row;}exactCheck([$expected],$rows);}
    // Existing sum/avg force float: record the legacy boundary, never promise exact aggregates.
    $phase='aggregate-legacy-boundary';exactCheck(true,is_float($db->table($table)->sum('d')));
    echo json_encode(['framework'=>$framework,'php'=>PHP_VERSION,'assertions'=>$assertions,'status'=>'pass','business_writes'=>0,'legacy_sum_float'=>true]).PHP_EOL;
}catch(Throwable $e){
    echo json_encode(['framework'=>$framework??null,'phase'=>$phase,'assertions'=>$assertions,'status'=>'fail','class'=>get_class($e),'code'=>$e->getCode()]).PHP_EOL;
    exit(1);
}
