<?php

declare(strict_types=1);

// Only the approved business-account POC processes are read. No SYSDBA, DDL or DML.
$stage = 'bootstrap';
set_exception_handler(static function (Throwable $error) use (&$stage): void {
    $info = $error instanceof PDOException ? $error->errorInfo : [];
    fwrite(STDERR, 'EXACT_TABLE_PREFLIGHT_FAILED ' . get_class($error)
        . ' stage=' . $stage . ' code=' . (string)($info[0] ?? $error->getCode())
        . PHP_EOL);
    exit(2);
});

$role = strtoupper((string)($argv[1] ?? ''));
$targets = [
    'STORE' => [
        'pid' => 112175, 'php' => '7.3.', 'http_port' => 18080,
        'root' => '/tmp/zhct-dm-poc-20260928.fcpiiN/store',
        'port' => '5237', 'schema' => 'ZHCT_APP',
        'table' => 'CDXODBC_20261002_E0E0F5CA', 'env_prefix' => 'POC_',
    ],
    'AI' => [
        'pid' => 53772, 'php' => '7.4.', 'http_port' => 18102,
        'root' => '/tmp/zhct-ai-http-20260929.3XDj3w',
        'port' => '5238', 'schema' => 'AIZHCT_APP',
        'table' => 'CDXODBC_20261002_B58E933C', 'env_prefix' => 'POC_AI_',
    ],
];
if (!isset($targets[$role])) throw new RuntimeException('Only STORE or AI role allowed');
$target = $targets[$role];
if (strpos(PHP_VERSION, $target['php']) !== 0
    || !preg_match('/^CDXODBC_20261002_[A-F0-9]{8}$/D', $target['table'])) {
    throw new RuntimeException('Runtime ABI or exact target invalid');
}
$pid = $target['pid'];
$env = [];
foreach (explode("\0", (string)@file_get_contents('/proc/' . $pid . '/environ')) as $item) {
    $parts = explode('=', $item, 2);
    if (count($parts) === 2) $env[$parts[0]] = $parts[1];
}
$cmdline = str_replace("\0", ' ',
    (string)@file_get_contents('/proc/' . $pid . '/cmdline'));
$expectedDocroot = $target['root'] . '/public';
if (strpos($cmdline, '-S 127.0.0.1:' . $target['http_port']) === false
    || strpos($cmdline, $expectedDocroot) === false) {
    throw new RuntimeException('Approved isolated HTTP process changed');
}
$prefix = $target['env_prefix'];
$get = static function (string $suffix) use ($env, $prefix): string {
    return (string)($env[$prefix . $suffix] ?? '');
};
$expectedRoot = $target['root'];
$baseUrl = (string)($env['POC_BASE_URL'] ?? '');
$baseParts = parse_url($baseUrl);
$pureLoopbackBase = is_array($baseParts)
    && ($baseParts['scheme'] ?? '') === 'http'
    && in_array(($baseParts['host'] ?? ''), ['127.0.0.1', 'localhost'], true)
    && isset($baseParts['port']) && (int)$baseParts['port'] > 0
    && (int)$baseParts['port'] <= 65535
    && in_array(($baseParts['path'] ?? ''), ['', '/'], true)
    && !isset($baseParts['query']) && !isset($baseParts['user'])
    && !isset($baseParts['pass']) && !isset($baseParts['fragment']);
$baseListenerPortSame = $pureLoopbackBase
    && (int)$baseParts['port'] === $target['http_port'];
if ($get('HOST') !== '127.0.0.1'
    || $get('PORT') !== $target['port']
    || $get('SCHEMA') !== $target['schema']
    || $get('USER') !== $target['schema']
    || $get('PASSWORD') === '') {
    throw new RuntimeException('Approved non-production DM connection changed');
}
if ($role === 'AI') {
    if (($env['POC_TARGET_ROOT'] ?? '') !== $expectedRoot
        || !$pureLoopbackBase
        || ($env['POC_DIALECT'] ?? '') !== 'dm') {
        throw new RuntimeException('AI HTTP boundary changed');
    }
} else {
    if (($env['POC_TARGET_ROOT'] ?? '') !== $expectedRoot
        || !$pureLoopbackBase
        || ($env['POC_DIALECT'] ?? '') !== 'dm') {
        throw new RuntimeException('Store HTTP boundary changed');
    }
}
if (!in_array('dm', PDO::getAvailableDrivers(), true)) {
    throw new RuntimeException('Private read-only DM PDO driver absent');
}
$stage = 'business_account_connection';
$db = new PDO('dm:host=127.0.0.1;port=' . $target['port']
    . ';schema=' . $target['schema'], $get('USER'), $get('PASSWORD'),
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_CASE => PDO::CASE_LOWER]);
$currentSchema = (string)$db->query("SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")
    ->fetchColumn();
$currentUser = (string)$db->query('SELECT USER FROM DUAL')->fetchColumn();
if ($currentSchema !== $target['schema'] || $currentUser !== $target['schema']) {
    throw new RuntimeException('Business-account owner/current schema mismatch');
}
$count = static function (string $sql, array $params = []) use ($db): int {
    $stmt = $db->prepare($sql);
    $stmt->execute($params);
    return (int)$stmt->fetchColumn();
};
$stage = 'exact_object_absence';
$userTables = $count('SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=?',
    [$target['table']]);
$userObjects = $count('SELECT COUNT(*) FROM USER_OBJECTS WHERE OBJECT_NAME=?',
    [$target['table']]);
$allTables = $count('SELECT COUNT(*) FROM ALL_TABLES'
    . ' WHERE OWNER=? AND TABLE_NAME=?', [$target['schema'], $target['table']]);
$directCreate = null;
try {
    $directCreate = $count('SELECT COUNT(*) FROM USER_SYS_PRIVS'
        . " WHERE PRIVILEGE='CREATE TABLE'") > 0;
} catch (Throwable $ignored) {
    // A missing/limited privilege view is unknown, never interpreted as denial.
}
$stage = 'private_module_static';
$privateOdbc = '/tmp/zhct-pdo-odbc-origin.QBjeLalA/build/modules/pdo_odbc.so';
$privateOdbcSha = 'cbc8889f144b53890059b0878fa8eb1e2e362f0e45b7f2f3aeb98cf8b9c8b4f7';
$odbcFileReady = is_file($privateOdbc)
    && hash_equals($privateOdbcSha, (string)hash_file('sha256', $privateOdbc));
$odbcRootMode = @fileperms('/tmp/zhct-pdo-odbc-origin.QBjeLalA');
$odbcPrivateDir = $odbcRootMode !== false && ($odbcRootMode & 0777) === 0700;
$dsnRootMode = @fileperms('/tmp/zhct-pdo-odbc-00w7A4sc/odbc-config');
$dsnPrivateDir = $dsnRootMode !== false && ($dsnRootMode & 0777) === 0700;
$dsnModes = [];
foreach (['odbc.ini', 'odbcinst.ini'] as $file) {
    $mode = @fileperms('/tmp/zhct-pdo-odbc-00w7A4sc/odbc-config/' . $file);
    $dsnModes[$file] = $mode !== false && ($mode & 0777) === 0600;
}
$privateAiDsnReady = false;
if ($dsnPrivateDir && !in_array(false, $dsnModes, true)) {
    $dsn = @parse_ini_file('/tmp/zhct-pdo-odbc-00w7A4sc/odbc-config/odbc.ini',
        true, INI_SCANNER_RAW);
    $drivers = @parse_ini_file('/tmp/zhct-pdo-odbc-00w7A4sc/odbc-config/odbcinst.ini',
        true, INI_SCANNER_RAW);
    $section = is_array($dsn) && isset($dsn['CDX_PRIVATE_AI'])
        ? array_change_key_case($dsn['CDX_PRIVATE_AI'], CASE_UPPER) : [];
    $alias = (string)($section['DRIVER'] ?? '');
    $driverSection = is_array($drivers) && isset($drivers[$alias])
        ? array_change_key_case($drivers[$alias], CASE_UPPER) : [];
    $containsSecretKey = false;
    foreach (array_keys($section) as $key) {
        if (preg_match('/^(UID|PWD|PASSWORD|PASSWD|USERNAME|USER)$/i', (string)$key)) {
            $containsSecretKey = true;
        }
    }
    $privateAiDsnReady = ($section['SERVER'] ?? '') === '127.0.0.1'
        && (string)($section['TCP_PORT'] ?? '') === '5238'
        && !$containsSecretKey && $alias !== ''
        && isset($driverSection['DRIVER'])
        && is_file((string)$driverSection['DRIVER']);
}
$php73PrivateOdbcValidated = false; // No reviewed PHP 7.3 private build/test artifact.
$absent = $userTables === 0 && $userObjects === 0 && $allTables === 0;
echo json_encode([
    'mode' => 'read-only-business-account-preflight',
    'role' => $role, 'owner' => $target['schema'], 'exact_table' => $target['table'],
    'php_abi_expected' => true, 'isolated_http_pid' => $pid,
    'base_url_loopback_pure' => $pureLoopbackBase,
    'base_url_listener_port_same' => $baseListenerPortSame,
    'business_account_schema_verified' => true,
    'user_tables_count' => $userTables, 'user_objects_count' => $userObjects,
    'all_tables_owner_count' => $allTables, 'exact_target_absent' => $absent,
    'direct_create_table_privilege' => $directCreate,
    'private_php74_odbc_module_hash_ready' => $odbcFileReady,
    'private_odbc_dir_mode700' => $odbcPrivateDir,
    'private_dsn_dir_mode700' => $dsnPrivateDir,
    'private_dsn_files_mode600' => $dsnModes,
    'private_ai_dsn_loopback_target_ready' => $privateAiDsnReady,
    'private_store_dsn_ready' => false,
    'php73_private_odbc_validated' => $php73PrivateOdbcValidated,
    'sysdba_connected' => false, 'ddl_or_dml_executed' => false,
], JSON_UNESCAPED_SLASHES) . PHP_EOL;
exit($absent ? 0 : 1);
