<?php
declare(strict_types=1);

// Fixed, isolated B/C Store read fixture. No response values, credentials, or tokens are emitted.
$lane = (string)($argv[1] ?? '');
$mode = (string)($argv[2] ?? '');
if (count($argv) !== 3 || !in_array($lane, ['B', 'C'], true)
    || !in_array($mode, ['--preflight', '--run', '--audit'], true)
    || ($mode === '--run' && getenv('CDX_USER_ADMIN3_RUN') !== 'TWO_OWN_USERS_READ3_V1')) {
    echo json_encode(['status' => 'default_deny', 'connected' => false, 'http' => false, 'dml' => false]), PHP_EOL;
    exit(78);
}
$stage = 'bootstrap';
set_exception_handler(static function (Throwable $error) use (&$stage): void {
    echo json_encode(['status' => 'redacted_stop', 'stage' => $stage,
        'class' => get_class($error), 'message_sha' => hash('sha256', $error->getMessage())]), PHP_EOL;
    exit(2);
});
[$root, $pid, $port] = $lane === 'B'
    ? ['/tmp/zhct-http-store-mysql.dQTTBP', 10001, 18081]
    : ['/tmp/zhct-dm-poc-20260928.fcpiiN/store', 112175, 18080];
$base = 'http://127.0.0.1:' . $port;
if (PHP_VERSION_ID < 70300 || PHP_VERSION_ID >= 70400 || !is_dir($root) || is_link($root)) {
    throw new RuntimeException('PHP/runtime drift');
}
$cmdline = (string)@file_get_contents('/proc/' . $pid . '/cmdline');
$children = trim((string)@file_get_contents('/proc/' . $pid . '/task/' . $pid . '/children'));
$boundary = json_decode((string)@file_get_contents($base . '/__poc_boundary'), true);
if (strpos($cmdline, '127.0.0.1:' . $port) === false
    || strpos($cmdline, $root . '/public') === false || $children !== ''
    || !is_array($boundary) || ($boundary['external_stubs'] ?? null) !== true
    || ($boundary['redis_isolated'] ?? null) !== true
    || ($boundary['real_queue'] ?? true) !== false) {
    throw new RuntimeException('Isolated HTTP boundary drift');
}
$runtime = require $root . '/application/database.php';
if ($lane === 'B') {
    $local = require '/workspace/zhct/zhctproject/store/application/config/local/database.php';
    foreach (['hostname', 'database', 'username', 'password'] as $key) {
        if ((string)($runtime[$key] ?? '') !== (string)($local[$key] ?? '')) {
            throw new RuntimeException('Not code-local development MySQL');
        }
    }
    if ((int)($runtime['hostport'] ?? 3306) !== (int)($local['hostport'] ?? 3306)) {
        throw new RuntimeException('Not code-local development MySQL port');
    }
    $dsn = 'mysql:host=' . $runtime['hostname'] . ';port=' . $runtime['hostport']
        . ';dbname=' . $runtime['database'] . ';charset=' . ($runtime['charset'] ?? 'utf8');
    $table = 'ydy_user';
} else {
    foreach (['hostname' => 'POC_HOST', 'hostport' => 'POC_PORT', 'database' => 'POC_SCHEMA',
        'username' => 'POC_USER', 'password' => 'POC_PASSWORD'] as $key => $envKey) {
        if ((string)($runtime[$key] ?? '') !== (string)getenv($envKey)) {
            throw new RuntimeException('C DM worker database drift');
        }
    }
    if ((string)getenv('POC_HOST') !== '127.0.0.1' || (string)getenv('POC_PORT') !== '5237'
        || (string)getenv('POC_SCHEMA') !== 'ZHCT_APP') {
        throw new RuntimeException('Not isolated DM Store');
    }
    $dsn = 'dm:host=127.0.0.1;port=5237;schema=ZHCT_APP';
    $table = 'YDY_USER';
}
$connect = static function () use ($dsn, $runtime): PDO {
    return new PDO($dsn, (string)$runtime['username'], (string)$runtime['password'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_CASE => PDO::CASE_LOWER,
            PDO::ATTR_TIMEOUT => 5]);
};
$db = $connect();
$identity = $lane === 'B'
    ? (string)$db->query("SELECT CONCAT(@@server_uuid,'|',@@hostname,'|',@@port,'|',DATABASE())")->fetchColumn()
    : 'ZHCT_APP|5237|' . (string)$db->query("SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn();
$expectedIdentitySha = $lane === 'B'
    ? '003eb5aecf1cd4595b609c1f7f20b9ee325b578309fb9fe14fedaf77584fa401'
    : 'bc98485aed26beab348770a82ae38dbf922b04ab88a210cd7282c4caf0156194';
if (hash('sha256', $identity) !== $expectedIdentitySha) {
    throw new RuntimeException('Database identity drift');
}
$stage = 'metadata';
if ($lane === 'B') {
    $meta = $db->prepare('SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=?');
    $meta->execute([$table]);
} else {
    $meta = $db->prepare('SELECT COLUMN_NAME FROM USER_TAB_COLUMNS WHERE TABLE_NAME=?');
    $meta->execute([$table]);
}
$columns = array_map('strtoupper', $meta->fetchAll(PDO::FETCH_COLUMN));
foreach (['ID', 'UUID', 'ACCOUNT', 'PWD', 'SALT', 'NAME', 'TYPE', 'MEMBER_TYPE',
    'STATUS', 'CREATE_TIME', 'CREATE_BY', 'IS_DELETE'] as $required) {
    if (!in_array($required, $columns, true)) {
        throw new RuntimeException('Required user column absent');
    }
}
$moneyMetaSql = $lane === 'B'
    ? "SELECT COLUMN_NAME,DATA_TYPE,NUMERIC_PRECISION,NUMERIC_SCALE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=? AND COLUMN_NAME IN ('balance','pay_money') ORDER BY COLUMN_NAME"
    : "SELECT COLUMN_NAME,DATA_TYPE,DATA_PRECISION,DATA_SCALE FROM USER_TAB_COLUMNS WHERE TABLE_NAME=? AND COLUMN_NAME IN ('BALANCE','PAY_MONEY') ORDER BY COLUMN_NAME";
$moneyMetaQuery = $db->prepare($moneyMetaSql);
$moneyMetaQuery->execute([$table]);
$moneyMeta = $moneyMetaQuery->fetchAll(PDO::FETCH_ASSOC);
$count = static function (PDO $connection, string $sql, array $params = []): int {
    $statement = $connection->prepare($sql);
    $statement->execute($params);
    return (int)$statement->fetchColumn();
};
$select = static function (PDO $connection, string $sql, array $params = []): array {
    $statement = $connection->prepare($sql);
    $statement->execute($params);
    return $statement->fetchAll(PDO::FETCH_ASSOC);
};
$leftover = $count($db, 'SELECT COUNT(*) FROM ' . $table
    . " WHERE ACCOUNT LIKE 'CDXUA3%' OR CREATE_BY LIKE 'CDXUA3%'");
if ($mode === '--preflight') {
    echo json_encode(['status' => $leftover === 0 ? 'ready' : 'residue_stop',
        'lane' => $lane, 'marker_rows' => $leftover, 'required_columns' => 12,
        'money_columns' => $moneyMeta,
        'http' => false, 'dml' => false]), PHP_EOL;
    exit($leftover === 0 ? 0 : 2);
}
if ($mode === '--audit') {
    echo json_encode(['status' => $leftover === 0 ? 'independent_zero_pass' : 'residue_stop',
        'lane' => $lane, 'marker_rows' => $leftover, 'http' => false, 'dml' => false]), PHP_EOL;
    exit($leftover === 0 ? 0 : 2);
}
if ($leftover !== 0) {
    throw new RuntimeException('Previous fixture residue');
}
$marker = 'CDXUA3' . strtoupper(bin2hex(random_bytes(6)));
$actorUuid = $marker . 'A';
$targetUuid = $marker . 'T';
if ($count($db, 'SELECT COUNT(*) FROM ' . $table . ' WHERE UUID IN (?,?) OR ACCOUNT IN (?,?)',
    [$actorUuid, $targetUuid, $marker . 'A', $marker . 'T']) !== 0) {
    throw new RuntimeException('Fixture identity collision');
}
$stage = 'fixture_insert';
$insert = $db->prepare('INSERT INTO ' . $table
    . ' (UUID,ACCOUNT,PWD,SALT,NAME,TYPE,MEMBER_TYPE,STATUS,CREATE_TIME,CREATE_BY,IS_DELETE)'
    . ' VALUES (?,?,?,?,?,?,?,?,?,?,?)');
$created = [];
$uncertain = false;
$result = ['status' => 'failed', 'lane' => $lane, 'routes' => [], 'fixture_rows_created' => 0];
$failure = '';
$shape = null;
$shape = static function ($node) use (&$shape) {
    if (!is_array($node)) return gettype($node);
    if ($node === []) return [];
    if (array_keys($node) === range(0, count($node) - 1)) return ['[]' => $shape($node[0])];
    $out = [];
    foreach ($node as $key => $value) $out[(string)$key] = $shape($value);
    ksort($out);
    return $out;
};
$source = static function (PDO $connection, string $uuid) use ($select, $table): array {
    return $select($connection, 'SELECT * FROM ' . $table . ' WHERE UUID=?', [$uuid]);
};
try {
    $now = date('Y-m-d H:i:s');
    $insert->execute([$actorUuid, $marker . 'A', 'DISABLED', bin2hex(random_bytes(8)),
        $marker . 'A', 0, 0, 0, $now, $marker, 0]);
    $created[] = [$actorUuid, $marker . 'A'];
    $insert->execute([$targetUuid, $marker . 'T', 'DISABLED', bin2hex(random_bytes(8)),
        $marker . 'T', 1, 1, 1, $now, $marker, 0]);
    $created[] = [$targetUuid, $marker . 'T'];
    $actor = $select($connect(), 'SELECT ID FROM ' . $table
        . ' WHERE UUID=? AND ACCOUNT=? AND CREATE_BY=? AND TYPE=0',
        [$actorUuid, $marker . 'A', $marker]);
    $target = $source($connect(), $targetUuid);
    if (count($actor) !== 1 || count($target) !== 1
        || (string)$target[0]['account'] !== $marker . 'T'
        || (int)$target[0]['member_type'] !== 1 || (int)$target[0]['status'] !== 1) {
        throw new RuntimeException('Owned actor or target not uniquely inserted');
    }
    $result['fixture_rows_created'] = 2;
    $targetSha = hash('sha256', serialize($target));
    require_once $root . '/application/base/Security.php';
    $token = \app\base\Security::encrypt(implode("\t", [
        (string)$actor[0]['id'], $actorUuid, $marker . 'A', '0', '', '', $marker . 'A']));
    if (!is_string($token) || $token === '') throw new RuntimeException('Actor token unavailable');
    $send = static function (string $route, array $form) use ($base, $token, &$uncertain): array {
        if (!in_array($route, ['lists', 'info', 'users'], true)) throw new RuntimeException('Route denied');
        $payload = http_build_query($form, '', '&', PHP_QUERY_RFC3986);
        $context = stream_context_create(['http' => ['method' => 'POST', 'ignore_errors' => true,
            'follow_location' => 0, 'timeout' => 25,
            'header' => "Content-Type: application/x-www-form-urlencoded\r\ntoken: " . $token . "\r\n",
            'content' => $payload]]);
        $uncertain = true;
        $handle = @fopen($base . '/p/user/' . $route, 'rb', false, $context);
        $raw = is_resource($handle) ? stream_get_contents($handle, 2097153) : false;
        $responseHeaders = $http_response_header ?? [];
        $meta = is_resource($handle) ? stream_get_meta_data($handle) : [];
        if (is_resource($handle)) fclose($handle);
        preg_match('~HTTP/\S+\s+(\d+)~', (string)($responseHeaders[0] ?? ''), $match);
        if (!is_string($raw) || strlen($raw) > 2097152 || !empty($meta['timed_out'])
            || (int)($match[1] ?? 0) !== 200) throw new RuntimeException('HTTP incomplete');
        $body = json_decode($raw, true);
        if (!is_array($body) || json_last_error() !== JSON_ERROR_NONE) {
            throw new RuntimeException('HTTP non-JSON');
        }
        $uncertain = false;
        return $body;
    };
    $stage = 'http_lists';
    $lists = $send('lists', ['page' => 1, 'account' => $marker . 'T']);
    $listRows = $lists['data']['rows'] ?? null;
    if ((int)($lists['code'] ?? -1) !== 0 || !is_array($listRows)
        || count($listRows) !== 1 || (int)($lists['data']['total'] ?? -1) !== 1
        || (string)($listRows[0]['uuid'] ?? '') !== $targetUuid
        || (string)($listRows[0]['account'] ?? '') !== $marker . 'T') {
        throw new RuntimeException('Lists source or business failure');
    }
    $result['routes']['lists'] = ['source_match' => true, 'positive_rows' => 1,
        'shape' => $shape($lists), 'shape_sha' => hash('sha256', serialize($shape($lists)))];
    $stage = 'http_info';
    $info = $send('info', ['uuid' => $targetUuid]);
    if ((int)($info['code'] ?? -1) !== 0 || !is_array($info['data'] ?? null)
        || (string)($info['data']['uuid'] ?? '') !== $targetUuid
        || (string)($info['data']['account'] ?? '') !== $marker . 'T') {
        throw new RuntimeException('Info source or business failure');
    }
    foreach ($target[0] as $field => $value) {
        if (!array_key_exists($field, $info['data'])) {
            throw new RuntimeException('Info original row differs: ' . $field);
        }
        $actual = $info['data'][$field];
        if (in_array(strtolower((string)$field), ['balance', 'pay_money'], true)) {
            if (bccomp((string)$actual, (string)$value, 2) !== 0) {
                throw new RuntimeException('Info DECIMAL original value differs: ' . $field);
            }
        } elseif ((string)$actual !== (string)$value) {
            throw new RuntimeException('Info original row differs: ' . $field);
        }
    }
    $result['routes']['info'] = ['source_match' => true,
        'original_columns_equal' => count($target[0]),
        'shape' => $shape($info), 'shape_sha' => hash('sha256', serialize($shape($info)))];
    $stage = 'http_users';
    $users = $send('users', ['keyword' => $marker . 'T']);
    $userRows = $users['data'] ?? null;
    if ((int)($users['code'] ?? -1) !== 0 || !is_array($userRows)
        || count($userRows) !== 1 || (string)($userRows[0]['uuid'] ?? '') !== $targetUuid
        || (string)($userRows[0]['account'] ?? '') !== $marker . 'T'
        || ($userRows[0]['checked'] ?? null) !== false) {
        throw new RuntimeException('Users source or business failure');
    }
    $result['routes']['users'] = ['source_match' => true, 'positive_rows' => 1,
        'shape' => $shape($users), 'shape_sha' => hash('sha256', serialize($shape($users)))];
    if (hash('sha256', serialize($source($connect(), $targetUuid))) !== $targetSha) {
        throw new RuntimeException('Read paths changed target row');
    }
    $result['status'] = 'HTTP_PASS_PENDING_CLEANUP';
} catch (Throwable $error) {
    $failure = $stage . ':' . get_class($error) . ':' . hash('sha256', $error->getMessage());
}
if ($uncertain) {
    $result['status'] = 'UNCERTAIN_HTTP_RETAIN_FIXTURE';
    $result['marker'] = $marker;
    echo json_encode($result, JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(3);
}
$stage = 'cleanup';
foreach (array_reverse($created) as [$uuid, $account]) {
    $owned = $count($connect(), 'SELECT COUNT(*) FROM ' . $table
        . ' WHERE UUID=? AND ACCOUNT=? AND CREATE_BY=?', [$uuid, $account, $marker]);
    if ($owned !== 1) throw new RuntimeException('Cleanup ownership gate');
    $delete = $db->prepare('DELETE FROM ' . $table
        . ' WHERE UUID=? AND ACCOUNT=? AND CREATE_BY=?');
    $delete->execute([$uuid, $account, $marker]);
    if ($delete->rowCount() !== 1) throw new RuntimeException('Cleanup row count');
}
$fresh = $connect();
$remaining = $count($fresh, 'SELECT COUNT(*) FROM ' . $table
    . ' WHERE UUID IN (?,?) OR ACCOUNT IN (?,?) OR CREATE_BY=?',
    [$actorUuid, $targetUuid, $marker . 'A', $marker . 'T', $marker]);
if ($remaining !== 0) throw new RuntimeException('Cleanup independent readback');
$result['cleanup'] = ['independent_owned_rows' => 0];
if ($failure !== '') {
    $result['status'] = 'HTTP_FAIL_CLEANED';
    $result['failure'] = $failure;
} else {
    $result['status'] = 'HTTP_PASS_CLEANED';
}
echo json_encode($result, JSON_UNESCAPED_SLASHES), PHP_EOL;
exit($failure === '' ? 0 : 2);
