<?php
declare(strict_types=1);

// Public Think ORM-only synthetic SELECT probe. Difference from the known
// reproducer: test connection does not set ATTR_ORACLE_NULLS.
$phase = 'preflight';
try {
    $autoload = (string)getenv('DM_PROBE_AUTOLOAD');
    $mode = (string)getenv('DM_PROBE_MODE');
    if (PHP_VERSION_ID !== 70433 || !defined('PDO::EXTRA_DECIMAL')
        || !in_array($mode, ['default', 'paired'], true)
        || !is_file($autoload)
        || strpos((string)realpath($autoload), '/tmp/zhct-dm-poc-') !== 0) {
        throw new RuntimeException('Expected isolated ORM/runtime/mode');
    }
    foreach (['HOST', 'PORT', 'SCHEMA', 'USER', 'PASSWORD'] as $part) {
        if ((string)getenv('DM_PROBE_' . $part) === '') {
            throw new RuntimeException('Approved AI environment missing');
        }
    }
    if (!in_array((string)getenv('DM_PROBE_HOST'), ['127.0.0.1', 'localhost'], true)
        || getenv('DM_PROBE_PORT') !== '5238'
        || getenv('DM_PROBE_SCHEMA') !== 'AIZHCT_APP') {
        throw new RuntimeException('Only loopback non-production AI 5238 is allowed');
    }
    $phase = 'autoload';
    require $autoload;
    class ProbeNoNullsBuilder extends \think\db\Builder {}
    class ProbeNoNullsConnection extends \think\db\connector\Mysql
    {
        protected $params = [
            PDO::ATTR_CASE => PDO::CASE_LOWER,
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_STRINGIFY_FETCHES => false,
            PDO::ATTR_EMULATE_PREPARES => false,
        ];
        public function getBuilderClass(): string { return ProbeNoNullsBuilder::class; }
        protected function parseDsn(array $config): string {
            return 'dm:host=' . $config['hostname'] . ';port=5238;schema=AIZHCT_APP';
        }
    }
    $params = [
        PDO::ATTR_STRINGIFY_FETCHES => $mode === 'paired',
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ];
    if ($mode === 'paired') {
        $params[PDO::EXTRA_DECIMAL] = 1;
    }
    $manager = new \think\DbManager();
    $manager->setConfig(['default' => 'probe', 'connections' => ['probe' => [
        'type' => '\\' . ProbeNoNullsConnection::class,
        'hostname' => getenv('DM_PROBE_HOST'),
        'hostport' => 5238,
        'database' => 'AIZHCT_APP',
        'username' => getenv('DM_PROBE_USER'),
        'password' => getenv('DM_PROBE_PASSWORD'),
        'prefix' => '',
        'debug' => false,
        'params' => $params,
    ]]]);
    $phase = 'connector-factory';
    $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";
    $table = '(' . $sql . ') EXACT_Q';
    foreach (['query', 'find', 'select'] as $phase) {
        fwrite(STDERR, 'ORM_NO_NULLS_BEGIN ' . $phase . PHP_EOL);
        if ($phase === 'query') {
            $rows = $db->query($sql);
        } elseif ($phase === 'find') {
            $rows = $db->table($table)->where('i', 7)->find();
        } else {
            $rows = $db->table($table)->select()->toArray();
        }
        $row = $phase === 'find' ? $rows : ($rows[0] ?? []);
        $types = [];
        foreach ($row ?? [] as $name => $value) {
            $types[$name] = gettype($value);
        }
        if (!$row || !isset($types['i'], $types['d'], $types['z'])) {
            throw new RuntimeException('Synthetic result missing');
        }
        if ($mode === 'paired' && ($row['i'] !== '7'
            || $row['d'] !== '9007199254740993.01' || $row['z'] !== '0.00')) {
            throw new RuntimeException('Exact synthetic result differs');
        }
        fwrite(STDERR, 'ORM_NO_NULLS_DONE ' . $phase . ' types=' . json_encode($types) . PHP_EOL);
    }
    echo json_encode(['mode' => 'orm_' . $mode . '_without_oracle_nulls',
        'php' => PHP_VERSION, 'status' => 'pass', 'business_writes' => 0]) . PHP_EOL;
} catch (Throwable $error) {
    echo json_encode(['mode' => 'orm_' . ($mode ?? 'unknown') . '_without_oracle_nulls',
        'php' => PHP_VERSION, 'status' => 'fail', 'phase' => $phase,
        'class' => get_class($error), 'code' => $error->getCode()]) . PHP_EOL;
    exit(1);
}
