<?php
declare(strict_types=1);

// READ ONLY. This file contains no DDL/DML/backup execution code.
$stage = 'entry';
set_exception_handler(static function (Throwable $error) use (&$stage): void {
    echo json_encode(['status' => 'redacted_error', 'stage' => $stage,
        'class' => get_class($error), 'code' => (string)$error->getCode(),
        'ddl' => false, 'dml' => false, 'backup_tool_started' => false]), PHP_EOL;
    exit(2);
});
if (($argv[1] ?? '') !== '--preflight'
    || getenv('CDX_RO_RECHARGE_PREFLIGHT') !== 'APPROVED_READONLY_V1'
    || !hash_equals((string)hash_file('sha256', __FILE__),
        (string)getenv('CDX_RO_RECHARGE_SHA'))) {
    echo json_encode(['status' => 'default_deny', 'ddl' => false,
        'dml' => false, 'backup_tool_started' => false]), PHP_EOL;
    exit(78);
}
$stage = 'fixed_host_runtime';
if (PHP_VERSION_ID !== 70433
    || !hash_equals('bcf83be7714eaec2734fb7dccddabc18c83c34b4201ee567f5044fb4bd54f526',
        hash('sha256', php_uname('n')))) {
    throw new RuntimeException('Unapproved host/PHP');
}
$pid = 53772;
$cmdline = str_replace("\0", ' ', (string)@file_get_contents('/proc/' . $pid . '/cmdline'));
$raw = (string)@file_get_contents('/proc/' . $pid . '/environ');
if (strpos($cmdline, '-S 127.0.0.1:18102') === false
    || strpos($cmdline, '/tmp/zhct-ai-http-20260929.3XDj3w/public') === false
    || $raw === '' || strpos($raw, 'PHP_CLI_SERVER_WORKERS=') !== false) {
    throw new RuntimeException('Isolated AI runtime changed');
}
$env = [];
foreach (explode("\0", $raw) as $entry) {
    if (strpos($entry, 'POC_') !== 0) continue;
    $equal = strpos($entry, '=');
    if ($equal !== false) $env[substr($entry, 0, $equal)] = substr($entry, $equal + 1);
}
foreach (['POC_DIALECT' => 'dm', 'POC_EXTERNAL_STUBS' => '1',
    'POC_AI_HOST' => '127.0.0.1', 'POC_AI_PORT' => '5238',
    'POC_AI_SCHEMA' => 'AIZHCT_APP', 'POC_AI_USER' => 'AIZHCT_APP'] as $key => $value) {
    if (($env[$key] ?? null) !== $value) throw new RuntimeException('AI target changed');
}
if (empty($env['POC_AI_PASSWORD'])) throw new RuntimeException('Private credential unavailable');
$lines = [];
$ssExit = 0;
@exec('/usr/sbin/ss -ltnp 2>/dev/null', $lines, $ssExit);
$listenerLocal = false;
if ($ssExit === 0) foreach ($lines as $line) {
    if (preg_match('/\*:5238\s+.*users:\(\("dmserver",pid=(\d+),/', $line, $match)) {
        $exe = @readlink('/proc/' . $match[1] . '/exe');
        $listenerLocal = is_string($exe) && basename($exe) === 'dmserver';
        break;
    }
}
if (!$listenerLocal) throw new RuntimeException('Local DM5238 listener absent');
$stage = 'business_connection';
$db = new PDO('dm:host=127.0.0.1;port=5238;schema=AIZHCT_APP',
    (string)$env['POC_AI_USER'], (string)$env['POC_AI_PASSWORD'],
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_CASE => PDO::CASE_LOWER, PDO::ATTR_TIMEOUT => 5]);
$currentUser = strtoupper((string)$db->query('SELECT USER FROM DUAL')->fetchColumn());
$currentSchema = strtoupper((string)$db->query(
    "SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn());
if ($currentUser !== 'AIZHCT_APP' || $currentSchema !== 'AIZHCT_APP') {
    throw new RuntimeException('Business owner/schema mismatch');
}
$table = 'YOSHOP_RECHARGE_ORDER';
$stage = 'metadata';
$stmt = $db->prepare('SELECT COLUMN_NAME,DATA_TYPE,DATA_LENGTH,DATA_PRECISION,'
    . ' DATA_SCALE,NULLABLE,DATA_DEFAULT,COLUMN_ID FROM USER_TAB_COLUMNS'
    . ' WHERE TABLE_NAME=? ORDER BY COLUMN_ID');
$stmt->execute([$table]);
$metadata = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (count($metadata) !== 28) throw new RuntimeException('Original 28-column schema changed');
$names = array_map(static function (array $row): string {
    return strtoupper((string)$row['column_name']);
}, $metadata);
$nameSha = hash('sha256', implode(',', $names));
if (!hash_equals('9e19cc62ce9c41a5b3167d22924c272e5795763fcc3b5c34c60ed84006b8564b',
    $nameSha)
    || in_array('WITHDRAWABLE_AMOUNT', $names, true)
    || in_array('ACTIVITY_PRINCIPAL_AMOUNT', $names, true)
    || ($names[9] ?? '') !== 'ACTUAL_MONEY'
    || ($names[10] ?? '') !== 'RECHARG_BEFORE') {
    throw new RuntimeException('Expected two-column gap changed');
}
$schemaMeta = [];
foreach ($metadata as $column) {
    $normalized = [];
    foreach ($column as $key => $value) {
        if (is_resource($value)) $value = stream_get_contents($value);
        $normalized[$key] = $value === null ? null : (string)$value;
    }
    $schemaMeta[] = $normalized;
}
$schemaSha = hash('sha256', json_encode($schemaMeta, JSON_UNESCAPED_SLASHES));
$stage = 'objects';
$objects = [];
foreach (['USER_INDEXES' => 'INDEX_NAME', 'USER_CONSTRAINTS' => 'CONSTRAINT_NAME',
    'USER_TRIGGERS' => 'TRIGGER_NAME'] as $view => $column) {
    $stmt = $db->prepare('SELECT ' . $column . ' FROM ' . $view
        . ' WHERE TABLE_NAME=? ORDER BY ' . $column);
    $stmt->execute([$table]);
    $objects[$view] = $stmt->fetchAll(PDO::FETCH_COLUMN);
}
if ($objects['USER_TRIGGERS'] !== []) throw new RuntimeException('Table trigger appeared');
$objectSha = hash('sha256', json_encode($objects, JSON_UNESCAPED_SLASHES));
$stage = 'full_original_data_digest';
$sql = 'SELECT ' . implode(',', array_map(static function (string $name): string {
    return '"' . $name . '"';
}, $names)) . ' FROM AIZHCT_APP.' . $table . ' ORDER BY ORDER_ID';
$stmt = $db->query($sql);
$hash = hash_init('sha256');
$rows = 0;
while ($row = $stmt->fetch(PDO::FETCH_NUM)) {
    foreach ($row as &$value) if (is_resource($value)) $value = stream_get_contents($value);
    unset($value);
    hash_update($hash, serialize($row) . "\n");
    $rows++;
    if ($rows > 1260) throw new RuntimeException('Original row count grew');
}
$dataSha = hash_final($hash);
if ($rows !== 1260) throw new RuntimeException('Original row count changed');
$uniqueOrderIds = (int)$db->query('SELECT COUNT(DISTINCT ORDER_ID)'
    . ' FROM AIZHCT_APP.' . $table)->fetchColumn();
if ($uniqueOrderIds !== $rows) throw new RuntimeException('ORDER_ID not unique for digest');
$stage = 'permission_and_space';
$directCreate = null;
try {
    $stmt = $db->query("SELECT COUNT(*) FROM USER_SYS_PRIVS WHERE PRIVILEGE='CREATE TABLE'");
    $directCreate = (int)$stmt->fetchColumn() > 0;
} catch (Throwable $ignored) {}
$backupRoot = '/dmdata/zhct_compat/codex_backups';
$backupMode = @fileperms($backupRoot);
$backupRootSafe = realpath($backupRoot) === $backupRoot
    && is_dir($backupRoot) && !is_link($backupRoot)
    && $backupMode !== false && ($backupMode & 0777) === 0700;
$free = $backupRootSafe ? disk_free_space($backupRoot) : false;
$stmt = $db->prepare('SELECT SUM(BYTES) FROM USER_SEGMENTS WHERE SEGMENT_NAME=?'
    . ' OR SEGMENT_NAME IN (SELECT INDEX_NAME FROM USER_INDEXES WHERE TABLE_NAME=?)');
$stmt->execute([$table, $table]);
$segmentBytes = (int)$stmt->fetchColumn();
$spaceReady = $backupRootSafe && $free !== false
    && $free > max(1024 * 1024 * 1024, $segmentBytes * 10);
$dmParameters = [];
foreach (['ALTER_TABLE_OPT', 'DDL_AUTO_COMMIT', 'ENABLE_DDL_ANY_PRIV'] as $parameter) {
    try {
        $stmt = $db->prepare('SELECT PARA_VALUE FROM V$DM_INI WHERE PARA_NAME=?');
        $stmt->execute([$parameter]);
        $value = $stmt->fetchColumn();
        $dmParameters[$parameter] = $value === false ? null : (string)$value;
    } catch (Throwable $ignored) {
        $dmParameters[$parameter] = 'unavailable';
    }
}
$backupManifests = $backupRootSafe
    ? (glob($backupRoot . '/recharge_order_*/manifest.json') ?: []) : [];
echo json_encode(['status' => 'read_only_preflight_pass',
    'target' => 'nonprod-ai-5238/AIZHCT_APP.YOSHOP_RECHARGE_ORDER',
    'business_account_owner' => true, 'listener_local' => true,
    'column_count' => 28, 'column_name_sha256' => $nameSha,
    'schema_metadata_sha256' => $schemaSha,
    'original_rows' => $rows, 'order_id_unique' => true,
    'original_28_column_data_sha256' => $dataSha,
    'object_counts' => array_map('count', $objects), 'objects_sha256' => $objectSha,
    'direct_create_table_privilege' => $directCreate,
    'backup_root_mode700' => $backupRootSafe,
    'free_bytes' => $free, 'table_and_indexes_bytes' => $segmentBytes,
    'capacity_gate' => $spaceReady,
    'dm_parameters' => $dmParameters,
    'existing_target_backup_manifest_count' => count($backupManifests),
    'mutating_stages_compiled_enabled' => false,
    'ddl' => false, 'dml' => false, 'backup_tool_started' => false],
    JSON_UNESCAPED_SLASHES) . PHP_EOL;
