<?php

declare(strict_types=1);

// Read-only synthetic-constant probe. No customer rows and no DML/DDL.
$private = (string)getenv('CDX_ODBC_PRIVATE_DIR');
$pid = (int)getenv('CDX_AI_POC_PID');
if (!preg_match('~^/tmp/zhct-pdo-odbc-[A-Za-z0-9]{8}$~D', $private)
    || !is_dir($private . '/odbc-config') || $pid <= 1) {
    throw new RuntimeException('Private ODBC probe boundary unavailable');
}
$raw = file_get_contents('/proc/' . $pid . '/environ');
if ($raw === false) throw new RuntimeException('Approved AI POC process unavailable');
$env = [];
foreach (explode("\0", $raw) as $part) {
    if (strpos($part, 'POC_') !== 0) continue;
    $position = strpos($part, '=');
    if ($position !== false) $env[substr($part, 0, $position)] = substr($part, $position + 1);
}
if (($env['POC_DIALECT'] ?? '') !== 'dm'
    || ($env['POC_EXTERNAL_STUBS'] ?? '') !== '1'
    || empty($env['POC_AI_USER']) || empty($env['POC_AI_PASSWORD'])) {
    throw new RuntimeException('Process is not the approved isolated DM AI POC');
}
putenv('ODBCSYSINI=' . $private . '/odbc-config');
putenv('ODBCINI=' . $private . '/odbc-config/odbc.ini');
putenv('ODBCINSTINI=odbcinst.ini');

$stage = 'connect';
try {
    $pdo = new PDO('odbc:CDX_PRIVATE_AI', $env['POC_AI_USER'],
        $env['POC_AI_PASSWORD'], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
    $stage = 'synthetic_select';
    $sql = "SELECT "
        . "CAST('9007199254740993.01' AS DECIMAL(30,2)) AS DEC30, "
        . "CAST(CAST('9007199254740993.01' AS DECIMAL(30,2)) AS VARCHAR(64)) AS REF_DEC30, "
        . "CAST('9007199254740993' AS DECIMAL(20,0)) AS DEC20, "
        . "CAST(CAST('9007199254740993' AS DECIMAL(20,0)) AS VARCHAR(64)) AS REF_DEC20, "
        . "CAST('7' AS INT) AS INT_VALUE, "
        . "CAST(CAST('7' AS INT) AS VARCHAR(64)) AS REF_INT, "
        . "CAST('1.25' AS FLOAT) AS FLOAT_VALUE, "
        . "CAST(CAST('1.25' AS FLOAT) AS VARCHAR(64)) AS REF_FLOAT, "
        . "NULL AS NULL_VALUE, "
        . "(SELECT COUNT(*) FROM DUAL) AS COUNT_VALUE, "
        . "CAST((SELECT COUNT(*) FROM DUAL) AS VARCHAR(64)) AS REF_COUNT, "
        . "(SELECT SUM(CAST('1.20' AS DECIMAL(5,2))) FROM DUAL) AS SUM_VALUE, "
        . "CAST((SELECT SUM(CAST('1.20' AS DECIMAL(5,2))) FROM DUAL) AS VARCHAR(64)) AS REF_SUM, "
        . "CAST('2026-10-01 12:34:56' AS DATETIME) AS DATE_VALUE, "
        . "CAST(CAST('2026-10-01 12:34:56' AS DATETIME) AS VARCHAR(64)) AS REF_DATE "
        . "FROM DUAL";
    $statement = $pdo->query($sql);
    $meta = [];
    for ($index = 0; $index < $statement->columnCount(); $index++) {
        try {
            $column = $statement->getColumnMeta($index);
            if ($column === false) {
                $meta[] = ['index' => $index, 'supported' => false];
                continue;
            }
            $selected = [];
            foreach (['name', 'native_type', 'pdo_type', 'len', 'precision',
                'scale', 'nullable', 'odbc:sql_type', 'odbc:column_size',
                'odbc:nullable_code'] as $key) {
                if (array_key_exists($key, $column)) $selected[$key] = $column[$key];
            }
            $meta[] = ['index' => $index, 'supported' => true, 'selected' => $selected];
        } catch (Throwable $error) {
            $meta[] = ['index' => $index, 'supported' => false,
                'exception' => get_class($error), 'code' => (string)$error->getCode()];
        }
    }
    $row = $statement->fetch(PDO::FETCH_ASSOC);
    if (!is_array($row)) throw new RuntimeException('Synthetic SELECT returned no row');
    $row = array_change_key_case($row, CASE_UPPER);
    $pairs = [
        'DEC30' => 'REF_DEC30', 'DEC20' => 'REF_DEC20',
        'INT_VALUE' => 'REF_INT', 'FLOAT_VALUE' => 'REF_FLOAT',
        'COUNT_VALUE' => 'REF_COUNT', 'SUM_VALUE' => 'REF_SUM',
        'DATE_VALUE' => 'REF_DATE',
    ];
    $results = [];
    foreach ($pairs as $name => $reference) {
        $value = $row[$name] ?? null;
        $exact = $value !== null && array_key_exists($reference, $row)
            && (string)$value === (string)$row[$reference];
        $results[$name] = ['php_type' => gettype($value),
            'value' => $value, 'reference' => $row[$reference] ?? null,
            'exact_string_match' => $exact];
    }
    $results['NULL_VALUE'] = ['php_type' => gettype($row['NULL_VALUE'] ?? null),
        'value_is_null' => ($row['NULL_VALUE'] ?? null) === null];
    $precisionOkay = $results['DEC30']['exact_string_match']
        && $results['DEC20']['exact_string_match'];
    $allExact = !in_array(false, array_column($results, 'exact_string_match'), true);
    $metadataOkay = !in_array(false, array_column($meta, 'supported'), true);
    echo json_encode(['status' => $precisionOkay && $allExact && $metadataOkay
            ? 'synthetic-precision-and-metadata-observed' : 'precision-or-metadata-fail',
        'driver' => $pdo->getAttribute(PDO::ATTR_DRIVER_NAME),
        'php_version' => PHP_VERSION, 'results' => $results,
        'column_meta' => $meta], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
    if (!$precisionOkay || !$allExact || !$metadataOkay) exit(3);
} catch (Throwable $error) {
    // Error messages can contain DSNs or private connection details: never print them.
    echo json_encode(['status' => 'probe-error', 'stage' => $stage,
        'exception' => get_class($error), 'code' => (string)$error->getCode()],
        JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(2);
}
