<?php
declare(strict_types=1);

// Separate process, fresh PDO, SELECT-only zero and baseline check.
$lane = (string)getenv('CDX_EM_DUAL_LANE');
$path = (string)getenv('CDX_EM_DUAL_JOURNAL');
if (!in_array($lane, ['A', 'B', 'C'], true)
    || count($argv) !== 2 || ($argv[1] ?? '') !== '--audit-zero'
    || (string)getenv('CDX_EM_DUAL_AUDIT') !== 'ROOT_APPROVED_DUAL_INDEPENDENT_ZERO_V1'
    || !hash_equals((string)hash_file('sha256', __FILE__),
        (string)getenv('CDX_EM_DUAL_AUDIT_SHA'))
    || !preg_match('~^/tmp/cdx-eboard-dual-cdxemdual[a-f0-9]{12}/recovery\.json$~D', $path)) {
    echo json_encode(['status' => 'default-deny', 'db_connected' => false,
        'business_http_sent' => false, 'dml_sent' => false]), PHP_EOL;
    exit(78);
}
$stage = 'journal';
set_exception_handler(static function (Throwable $error) use (&$stage): void {
    echo json_encode(['status' => 'redacted_stop', 'stage' => $stage,
        'error_class' => get_class($error), 'code' => (string)$error->getCode(),
        'journal_retained' => true]), PHP_EOL;
    exit(2);
});
if (is_link($path) || realpath($path) !== $path
    || is_link(dirname($path))
    || (((int)fileperms(dirname($path)) & 0077) !== 0)
    || (((int)fileperms($path) & 0077) !== 0)) {
    throw new RuntimeException('Private journal boundary unsafe');
}
$journal = json_decode((string)file_get_contents($path), true);
if (!is_array($journal) || ($journal['lane'] ?? '') !== $lane
    || !in_array(($journal['phase'] ?? ''),
        ['local_cleanup_checked_awaiting_independent_audit',
            'recovery_cleanup_checked_awaiting_independent_audit'], true)
    || !preg_match('/^CDXEMDUAL[A-F0-9]{12}$/D', (string)($journal['marker'] ?? ''))
    || !preg_match('/^[A-F0-9]{32}$/D', (string)($journal['uuid'] ?? ''))
    || !preg_match('/^[a-f0-9]{64}$/D', (string)($journal['public_token'] ?? ''))
    || (int)($journal['actor_id'] ?? 0) <= 0
    || (int)($journal['menu_id'] ?? 0) <= 0
    || !is_array($journal['source_before'] ?? null)) {
    throw new RuntimeException('Owned journal identity/phase invalid');
}
$stage = 'approved_database';
$root = ['A' => '/tmp/zhct-http-store-a.l5omH8',
    'B' => '/tmp/zhct-http-store-mysql.dQTTBP',
    'C' => '/tmp/zhct-dm-poc-20260928.fcpiiN/store'][$lane];
$dialect = $lane === 'C' ? 'dm' : 'mysql';
$runtime = require $root . '/application/database.php';
if (!is_array($runtime)) throw new RuntimeException('Runtime config unavailable');
if ($dialect === 'mysql') {
    $config = $runtime;
    $local = require '/workspace/zhct/zhctproject/store/application/config/local/database.php';
    foreach (['hostname', 'database', 'username', 'password'] as $key) {
        if (($runtime[$key] ?? null) !== ($local[$key] ?? null)) {
            throw new RuntimeException('Not code-local development MySQL');
        }
    }
    if ((int)($runtime['hostport'] ?? 0) !== 3306
        || (string)($runtime['type'] ?? '') !== 'mysql'
        || (isset($local['hostport']) && (int)$local['hostport'] !== 3306)
        || (isset($local['type']) && (string)$local['type'] !== 'mysql')) {
        throw new RuntimeException('Developer MySQL port or driver drift');
    }
    $dsn = 'mysql:host=' . $runtime['hostname'] . ';port=' . (int)$runtime['hostport']
        . ';dbname=' . $runtime['database'] . ';charset=' . ($runtime['charset'] ?? 'utf8');
    $dbUser = (string)$runtime['username'];
    $dbPassword = (string)$runtime['password'];
} else {
    foreach (['hostname' => 'POC_HOST', 'hostport' => 'POC_PORT',
        'database' => 'POC_SCHEMA', 'username' => 'POC_USER',
        'password' => 'POC_PASSWORD'] as $field => $envKey) {
        if ((string)($runtime[$field] ?? '') !== (string)getenv($envKey)) {
            throw new RuntimeException('Not approved nonproduction DM');
        }
    }
    if ((string)getenv('POC_HOST') !== '127.0.0.1'
        || (string)getenv('POC_PORT') !== '5237'
        || strtoupper((string)getenv('POC_SCHEMA')) !== 'ZHCT_APP') {
        throw new RuntimeException('Wrong C DM target');
    }
    $machine = trim((string)file_get_contents('/etc/machine-id'));
    if (gethostname() === false || hash('sha256', gethostname() . '|' . $machine)
        !== 'd56d81988bb0f106946cce91c608fdd0ef1116e5150b23376ea87e086eace4b9') {
        throw new RuntimeException('Wrong C DM host');
    }
    $dsn = 'dm:host=127.0.0.1;port=5237;schema=ZHCT_APP';
    $dbUser = (string)getenv('POC_USER');
    $dbPassword = (string)getenv('POC_PASSWORD');
}
$db = new PDO($dsn, $dbUser, $dbPassword,
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_CASE => PDO::CASE_LOWER, PDO::ATTR_TIMEOUT => 5]);
if ($dialect === 'mysql') {
    $identity = (string)$db->query(
        "SELECT CONCAT(@@server_uuid,'|',@@hostname,'|',@@port,'|',DATABASE())")
        ->fetchColumn();
    if (hash('sha256', $identity)
        !== '003eb5aecf1cd4595b609c1f7f20b9ee325b578309fb9fe14fedaf77584fa401') {
        throw new RuntimeException('Development MySQL identity drift');
    }
} else {
    $schema = strtoupper((string)$db->query(
        "SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn());
    if ($schema !== 'ZHCT_APP') throw new RuntimeException('DM schema drift');
}
$t = static function (string $name) use ($dialect): string {
    if (!in_array($name, ['user', 'electronic_menu',
        'electronic_menu_bind_code', 'electronic_menu_device',
        'electronic_menu_device_log'], true)) {
        throw new RuntimeException('Audit table not allowlisted');
    }
    return $dialect === 'dm' ? strtoupper('ydy_' . $name) : 'ydy_' . $name;
};
$stage = 'independent_zero_and_fingerprint';
$q = $db->prepare('SELECT COUNT(*) FROM ' . $t('user')
    . ' WHERE ID=? OR UUID=? OR ACCOUNT=?');
$q->execute([(int)$journal['actor_id'], $journal['marker'], $journal['marker']]);
$actorCount = (int)$q->fetchColumn();
$q = $db->prepare('SELECT COUNT(*) FROM ' . $t('electronic_menu')
    . ' WHERE ID=? OR UUID=? OR PUBLIC_TOKEN=? OR CREATE_BY=?');
$q->execute([(int)$journal['menu_id'], $journal['uuid'],
    $journal['public_token'], $journal['marker']]);
$menuCount = (int)$q->fetchColumn();
$relationCounts = [];
foreach (['electronic_menu_bind_code', 'electronic_menu_device',
    'electronic_menu_device_log'] as $name) {
    $q = $db->prepare('SELECT COUNT(*) FROM ' . $t($name) . ' WHERE MENU_ID=?');
    $q->execute([(int)$journal['menu_id']]);
    $relationCounts[$name] = (int)$q->fetchColumn();
}
if ($actorCount !== 0 || $menuCount !== 0 || array_sum($relationCounts) !== 0) {
    throw new RuntimeException('Owned rows remain after cleanup');
}
$expectedKeys = ['electronic_menu', 'electronic_menu_bind_code',
    'electronic_menu_device', 'electronic_menu_device_log'];
$actualKeys = array_keys($journal['source_before']);
sort($actualKeys);
$sortedExpected = $expectedKeys;
sort($sortedExpected);
if ($actualKeys !== $sortedExpected) {
    throw new RuntimeException('Baseline table set changed');
}
foreach ($expectedKeys as $name) {
    $actual = hash('sha256', serialize($db->query('SELECT * FROM '
        . $t($name) . ' ORDER BY ID')->fetchAll(PDO::FETCH_ASSOC)));
    if (!hash_equals((string)$journal['source_before'][$name], $actual)) {
        throw new RuntimeException('Original table baseline changed');
    }
}
if (!unlink($path) || !rmdir(dirname($path))) {
    throw new RuntimeException('Private journal removal failed');
}
echo json_encode(['status' => 'independent_zero_pass', 'lane' => $lane,
    'actor_rows' => 0, 'menu_rows' => 0,
    'relation_rows' => 0, 'four_table_fingerprints_restored' => true,
    'private_journal_removed' => true,
    'business_http_sent' => false, 'dml_sent' => false]), PHP_EOL;
