<?php

declare(strict_types=1);

// Detail-only GiftLog HTTP. No list endpoint and no business DML.
$requestedMode = (string)($argv[1] ?? '');
$lane = (string)getenv('CDX_GIFT_DETAIL_LANE');
if (!in_array($requestedMode, ['--preflight', '--read', '--token-zero'], true)
    || !in_array($lane, ['A', 'B', 'C'], true)
    || ($requestedMode !== '--preflight'
        && ((string)getenv('CDX_GIFT_DETAIL_HTTP_APPROVED')
                !== 'ROOT_APPROVED_GIFT_DETAIL_HTTP_V1'
            || (string)getenv('CDX_GIFT_DETAIL_HTTP_SHA')
                !== hash_file('sha256', __FILE__)))) {
    echo json_encode(['status' => 'default_deny',
        'business_http' => 0, 'db_dml' => 0, 'cache_token' => 0]), PHP_EOL;
    exit(78);
}
$preflight = $lane === 'C'
    ? '/tmp/zhct-giftlog-readonly-20261004/preflight_gift_log.php'
    : __DIR__ . '/../parallel-ai-gift-log-http/preflight_gift_log.php';
if (!is_file($preflight) || hash_file('sha256', $preflight)
    !== 'a54a040990767b0c539ef13ffa1cd8aed97b2dce5faa2d760a2c04ae195583c2') {
    throw new RuntimeException('Gift detail base preflight drift');
}
putenv('CDX_GIFT_LANE=' . $lane);
$argv[1] = '--preflight';
require $preflight;
$mode = $requestedMode;
$argv[1] = $requestedMode;
$stage = 'runtime_source';
set_exception_handler(static function (Throwable $error) use (&$stage): void {
    echo json_encode(['status' => 'gift_detail_http_error',
        'stage' => $stage, 'class' => get_class($error),
        'sql_code' => (string)$error->getCode(),
        'reason_sha' => hash('sha256', $error->getMessage()),
        'db_dml' => 0], JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(2);
});
$runtimeRoot = ['A' => '/private/tmp/zhct-http-ai-macos.zjaU8h',
    'B' => '/private/tmp/zhct-http-ai-macos.x2CCZX',
    'C' => '/tmp/zhct-ai-http-20260929.3XDj3w'][$lane];
$sourceRoot = ['A' => '/Users/wiscourper/Documents/workspace/local/dm8-full-acceptance.fF5yTy/baseline-a-ai-api-V4.0.40',
    'B' => '/Users/wiscourper/Documents/workspace/local/dm8-full-acceptance.fF5yTy/ai_api',
    'C' => '/tmp/zhct-dm-poc-20260928.fcpiiN/ai_api'][$lane];
$port = ['A' => 18105, 'B' => 18101, 'C' => 18102][$lane];
$expectedModel = $lane === 'A'
    ? 'cbe789c4d5d3f15ce99b3ac8a2ace58af9935d132edf70129f83cb7eb6ba2782'
    : '0e55edc5a3ea0a98eae2dee78500d3d2ba177a6ea059bdee077908d687862035';
$modelFile = $sourceRoot . '/app/api/model/user/GiftLog.php';
$controllerFile = $sourceRoot . '/app/api/controller/gift/Log.php';
$serviceFile = $sourceRoot . '/app/api/service/gift/Log.php';
if (!is_file($modelFile) || hash_file('sha256', $modelFile) !== $expectedModel
    || !is_file($controllerFile)
    || hash_file('sha256', $controllerFile)
        !== '9d70fbc0555f77697065ddb6568cf85cdc9cfdd7b6c69589e4fb617cdf592c4d'
    || !is_file($serviceFile)
    || hash_file('sha256', $serviceFile)
        !== '03c3af93dc4a08c0932ee573112dfe5ac9ad7e225c902ca18f0cb9de31df4681') {
    throw new RuntimeException('Gift detail actual Composer source drift');
}
$cacheRoot = realpath($runtimeRoot . '/runtime/cache');
if (realpath($runtimeRoot) !== $runtimeRoot
    || $cacheRoot !== $runtimeRoot . '/runtime/cache'
    || (((int)fileperms($runtimeRoot) & 0077) !== 0)
    || hash_file('sha256', $runtimeRoot . '/config/cache.php')
        !== 'ccf1c56eb4178f3eaad8775e26a27742b2cacf662b46fb058cbb91d23783b8c6'
    || is_file($runtimeRoot . '/.env') || is_file($runtimeRoot . '/.env.poc')
    || getenv('CACHE_DRIVER') !== false || getenv('cache.driver') !== false) {
    throw new RuntimeException('Gift detail File cache boundary drift');
}
foreach ($config as $key => $value) {
    if (strpos($key, 'POC_') === 0) putenv($key . '=' . $value);
}
define('TP_ENV', 'poc');
define('BUSINESS', 'aizhct');
define('ENTERPRISE_NO', '10001');
require $runtimeRoot . '/vendor/autoload.php';
if ((new ReflectionClass(\app\api\model\user\GiftLog::class))->getFileName()
    !== $modelFile) throw new RuntimeException('Gift detail Composer path drift');
(new \think\App($runtimeRoot))->setEnvName('poc')->initialize();
if (strtolower((string)\think\facade\Config::get('cache.default')) !== 'file'
    || realpath((string)\think\facade\Config::get('cache.stores.file.path'))
        !== $cacheRoot
    || (string)\think\facade\Config::get('cache.stores.file.prefix') !== '') {
    throw new RuntimeException('Gift detail effective File cache drift');
}
$strictIdentity = static function (PDO $pdo, string $side) use ($dialect): bool {
    if ($dialect === 'dm') {
        $expected = $side === 'ai' ? 'AIZHCT_APP' : 'ZHCT_APP';
        return strtoupper((string)$pdo->query('SELECT USER FROM DUAL')->fetchColumn())
                === $expected
            && strtoupper((string)$pdo->query(
                "SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn())
                === $expected;
    }
    $identity = (string)$pdo->query(
        "SELECT CONCAT(@@server_uuid,'|',@@hostname,'|',@@port,'|',DATABASE())")
        ->fetchColumn();
    $expected = $side === 'ai'
        ? '210f925e093f71e26a8c8923f0f626e3be0a15ccff3d386bba21feef695c8a5b'
        : '003eb5aecf1cd4595b609c1f7f20b9ee325b578309fb9fe14fedaf77584fa401';
    return hash_equals($expected, hash('sha256', $identity));
};
$ai->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
$store->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
if (!$strictIdentity($ai, 'ai') || !$strictIdentity($store, 'store')) {
    throw new RuntimeException('Gift detail initial database identity drift');
}
$shapeFile = $lane === 'C'
    ? '/tmp/zhct-giftlog-readonly-20261004/gift_contract_shape.php'
    : __DIR__ . '/../parallel-ai-gift-log-http/gift_contract_shape.php';
if (!is_file($shapeFile) || hash_file('sha256', $shapeFile)
    !== '03eefd38885b735d129b2835323adf4e275ba04c8777c23a775e24f72b3915a8') {
    throw new RuntimeException('Gift recursive contract shape drift');
}
require_once $shapeFile;
if ($mode === '--preflight' && (string)getenv('CDX_GIFT_DETAIL_JOURNAL') === '') {
    echo json_encode(['status' => 'gift_detail_http_runtime_preflight_pass',
        'lane' => $lane, 'model_sha' => $expectedModel,
        'cache_private' => true, 'fixture_not_created' => true,
        'business_http' => 0, 'db_dml' => 0, 'token' => 0],
        JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(0);
}
$journalPath = (string)getenv('CDX_GIFT_DETAIL_JOURNAL');
$dir = dirname($journalPath);
if (basename($journalPath) !== 'fixture.json'
    || strpos(basename($dir), 'cdx-gift-detail-CDXGD5') !== 0
    || !is_dir($dir) || is_link($dir)
    || !is_file($journalPath) || is_link($journalPath)
    || (((int)fileperms($dir) & 0077) !== 0)
    || (((int)fileperms($journalPath) & 0077) !== 0)) {
    throw new RuntimeException('Gift detail private journal unavailable');
}
$journal = json_decode((string)file_get_contents($journalPath), true);
if (!is_array($journal)
    || ($journal['mode'] ?? '') !== ($lane === 'C' ? 'C' : 'AB')
    || !preg_match('/^CDXGD5[0-9A-F]{12}$/', (string)($journal['marker'] ?? ''))
    || (int)($journal['log_id'] ?? 0) <= 0
    || ($journal['create_time_business'] ?? '') !== '2026-09-15 12:00:00') {
    throw new RuntimeException('Gift detail owned journal values drift');
}
$marker = (string)$journal['marker'];
$ownerNick = $lane === 'C' ? 'CDXPARMOB9411C2CB' : 'CDXPARMOB426CA4B0';
$foreignNick = $lane === 'C' ? 'CDXPAY661BEE6B' : 'CDXPAY112FC189';
$giftTable = $dialect === 'dm' ? 'YOSHOP_USER_GIFT_LOG' : 'yoshop_user_gift_log';
$userTable = $dialect === 'dm' ? 'YOSHOP_USER' : 'yoshop_user';
$storeUser = $dialect === 'dm' ? 'YDY_USER' : 'ydy_user';
$one = static function (PDO $pdo, string $sql, array $args): array {
    $stmt = $pdo->prepare($sql);
    $stmt->execute($args);
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    if (count($rows) !== 1) throw new RuntimeException('Gift detail source not unique');
    $out = [];
    foreach (array_change_key_case($rows[0], CASE_LOWER) as $name => $value) {
        $out[$name] = is_resource($value) ? stream_get_contents($value) : $value;
    }
    return $out;
};
$digest = static function (array $row): string {
    ksort($row, SORT_STRING);
    return hash('sha256', serialize($row));
};
$ownerId = (int)$journal['owner_id'];
$foreignId = (int)$journal['foreign_id'];
$owner = $one($ai, 'SELECT * FROM ' . $userTable
    . ' WHERE USER_ID=? AND NICK_NAME=? AND STORE_ID=10001 AND IS_DELETE=0',
    [$ownerId, $ownerNick]);
$foreign = $one($ai, 'SELECT * FROM ' . $userTable
    . ' WHERE USER_ID=? AND NICK_NAME=? AND STORE_ID=10001 AND IS_DELETE=0',
    [$foreignId, $foreignNick]);
$ownerMapping = $one($store, 'SELECT * FROM ' . $storeUser
    . ' WHERE AI_USER_ID=? AND IS_DELETE=0', [$ownerId]);
$foreignMapping = $one($store, 'SELECT * FROM ' . $storeUser
    . ' WHERE AI_USER_ID=? AND IS_DELETE=0', [$foreignId]);
$raw = $one($ai, 'SELECT * FROM ' . $giftTable
    . ' WHERE LOG_ID=? AND USER_ID=? AND IDEMPOTENCY_KEY=? AND STORE_ID=10001',
    [(int)$journal['log_id'], $ownerId, $marker . ':O']);
if ($digest($owner) !== (string)$journal['owner_user_sha']
    || $digest($foreign) !== (string)$journal['foreign_user_sha']
    || $digest($ownerMapping) !== (string)$journal['owner_store_sha']
    || $digest($foreignMapping) !== (string)$journal['foreign_store_sha']
    || $digest($raw) !== (string)$journal['log_sha']) {
    throw new RuntimeException('Gift detail user/mapping/log source drift');
}
if ($mode === '--preflight') {
    echo json_encode(['status' => 'gift_detail_http_fixture_preflight_pass',
        'lane' => $lane, 'owned_log_count' => 1,
        'owner_and_foreign_users_unique' => true,
        'source_hashes_locked' => true, 'business_http' => 0,
        'db_dml' => 0, 'token' => 0], JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(0);
}
$writeJournal = static function () use (&$journal, $dir, $journalPath): void {
    $temp = tempnam($dir, 'http-');
    if (!is_string($temp) || !chmod($temp, 0600)) {
        throw new RuntimeException('Gift detail journal temp unavailable');
    }
    $json = json_encode($journal, JSON_UNESCAPED_SLASHES);
    if (!is_string($json) || file_put_contents($temp, $json, LOCK_EX) !== strlen($json)
        || !rename($temp, $journalPath)) {
        throw new RuntimeException('Gift detail journal write failed');
    }
};
if ($mode === '--token-zero') {
    $tokens = $journal['tokens'][$lane] ?? null;
    if (!is_array($tokens) || count($tokens) !== 2) {
        throw new RuntimeException('No two detail tokens for independent zero');
    }
    foreach ($tokens as $token) {
        if (!is_string($token) || strlen($token) < 20
            || \think\facade\Cache::has($token)) {
            throw new RuntimeException('Gift detail token still present');
        }
    }
    unset($journal['tokens'][$lane]);
    $journal['token_states'][$lane] = 'zero';
    $writeJournal();
    echo json_encode(['status' => 'gift_detail_tokens_independent_zero',
        'lane' => $lane, 'token_count' => 2,
        'business_http' => 0, 'db_dml' => 0], JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(0);
}
if (isset($journal['tokens'][$lane]) || isset($journal['token_states'][$lane])) {
    throw new RuntimeException('Gift detail lane already attempted');
}
if ($lane === 'B'
    && (($journal['token_states']['A'] ?? '') !== 'zero'
        || empty($journal['contracts']['A']))) {
    throw new RuntimeException('Gift detail A result and zero required before B');
}
$aBaseline = $lane === 'B' ? ($journal['contracts']['A'] ?? null) : null;
if ($lane === 'C') {
    $contractFile = (string)getenv('CDX_GIFT_DETAIL_A_CONTRACT_FILE');
    $contractSha = (string)getenv('CDX_GIFT_DETAIL_A_CONTRACT_SHA');
    if (!is_file($contractFile) || is_link($contractFile)
        || (((int)fileperms($contractFile) & 0077) !== 0)
        || (((int)fileperms(dirname($contractFile)) & 0077) !== 0)
        || strlen($contractSha) !== 64
        || !hash_equals($contractSha, (string)hash_file('sha256', $contractFile))) {
        throw new RuntimeException('Gift detail private A contract absent');
    }
    $aBaseline = json_decode((string)file_get_contents($contractFile), true);
    if (!is_array($aBaseline) || ($aBaseline['lane'] ?? '') !== 'A') {
        throw new RuntimeException('Gift detail A type contract invalid');
    }
}
$requestComplete = true;
$requests = [];
$request = static function (string $token) use ($port, $journalPath,
    &$journal, $writeJournal, $lane, &$requestComplete, &$requests): array {
    $headers = "platform: MP-WEIXIN\r\nversion: 2.0.51\r\nenv-version: develop\r\n";
    if ($token !== '') $headers .= 'Access-Token: ' . $token . "\r\n";
    $context = stream_context_create(['http' => [
        'method' => 'GET', 'timeout' => 15, 'ignore_errors' => true,
        'follow_location' => 0, 'max_redirects' => 0,
        'header' => $headers]]);
    $journal['http_states'][$lane] = 'inflight';
    $writeJournal();
    $requestComplete = false;
    $url = 'http://127.0.0.1:' . $port . '/api/gift.Log/detail?'
        . http_build_query(['storeId' => 10001,
            'logId' => (int)$journal['log_id'],
            'log_id' => (int)$journal['log_id']], '', '&', PHP_QUERY_RFC3986);
    $stream = @fopen($url, 'rb', false, $context);
    $body = is_resource($stream) ? stream_get_contents($stream, 65537) : false;
    $meta = is_resource($stream) ? stream_get_meta_data($stream) : [];
    if (is_resource($stream)) fclose($stream);
    if (!is_string($body) || !empty($meta['timed_out']) || strlen($body) > 65536) {
        $journal['http_states'][$lane] = 'unknown';
        $writeJournal();
        throw new RuntimeException('Gift detail HTTP outcome unknown or oversized');
    }
    $header = $http_response_header[0] ?? '';
    $status = preg_match('/HTTP\/\S+\s+(\d+)/', (string)$header, $m)
        ? (int)$m[1] : 0;
    $json = json_decode($body, true);
    if ($status === 0 || !is_array($json)
        || !array_key_exists('status', $json)) {
        $journal['http_states'][$lane] = 'unknown';
        $writeJournal();
        throw new RuntimeException('Gift detail HTTP body not confirmed JSON');
    }
    $requestComplete = true;
    $journal['http_states'][$lane] = 'confirmed';
    $writeJournal();
    $requests[] = ['http' => $status, 'business_status' => (int)$json['status']];
    return [$json, $body, $status];
};
$tokenValues = [];
$failure = '';
$contract = null;
$tokensDeleted = false;
try {
    $stage = 'short_token_creation';
    foreach (['owner', 'foreign'] as $role) {
        $tokenValues[$role] = 'CDXGD5T' . strtoupper(bin2hex(random_bytes(16)));
        $journal['tokens'][$lane][$role] = $tokenValues[$role];
    }
    $journal['token_states'][$lane] = 'issued';
    $writeJournal();
    foreach ($tokenValues as $role => $token) {
        $id = $role === 'owner' ? $ownerId : $foreignId;
        $model = \app\api\model\User::detail($id);
        if (empty($model) || (int)$model['user_id'] !== $id) {
            throw new RuntimeException('Controlled detail auth model unavailable');
        }
        $mapping = $role === 'owner' ? $ownerMapping : $foreignMapping;
        if (!\think\facade\Cache::set($token, [
            'user' => $model, 'store_id' => 10001, 'is_login' => true,
            'password_version' => (int)$mapping['password_version'],
            'login_type' => 'sms'], 300)
            || !\think\facade\Cache::has($token)) {
            throw new RuntimeException('Gift detail isolated token unavailable');
        }
    }
    $stage = 'owned_detail_http';
    [$json, $body, $httpStatus] = $request($tokenValues['owner']);
    $item = $json['data']['data'] ?? null;
    if ($httpStatus !== 200 || (int)$json['status'] !== 200
        || !is_array($item)
        || (int)($item['log_id'] ?? 0) !== (int)$raw['log_id']
        || (int)($item['user_id'] ?? 0) !== $ownerId
        || (int)($item['scene'] ?? 0) !== 20) {
        throw new RuntimeException('Owned Gift detail HTTP/identity contract mismatch');
    }
    foreach (['money', 'balance_before', 'balance_after'] as $money) {
        if (!is_string($item[$money] ?? null)
            || !preg_match('/^-?\d+\.\d{2}$/', $item[$money])
            || !function_exists('bccomp')
            || bccomp($item[$money], (string)$raw[$money], 2) !== 0) {
            throw new RuntimeException('Gift detail money not exact two-place JSON string');
        }
    }
    foreach (['store_id', 'idempotency_key', 'is_delete',
        'create_by', 'update_time'] as $hidden) {
        if (array_key_exists($hidden, $item)) {
            throw new RuntimeException('Gift detail internal column leaked');
        }
    }
    if ((string)($item['create_time'] ?? '')
            !== (string)$journal['create_time_business']
        || (string)($item['order_no'] ?? '') !== (string)$raw['biz_order_no']
        || (string)($item['pay_method'] ?? '__MISSING__') !== '') {
        throw new RuntimeException('Gift detail legal date or derived field mismatch');
    }
    $normalized = $json;
    unset($normalized['trace_id']);
    $contract = ['business_sha' => hash('sha256', json_encode($normalized,
            JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)),
        'type_map' => giftLogJsonShape($body)];
    if ($lane === 'B' && $contract !== ($aBaseline['detail'] ?? null)) {
        throw new RuntimeException('A/B full normalized Gift detail differs');
    }
    if ($lane === 'C'
        && $contract['type_map'] !== ($aBaseline['detail']['type_map'] ?? null)) {
        throw new RuntimeException('C full recursive Gift detail type contract differs');
    }
    $stage = 'cross_user_and_noauth';
    [$cross] = $request($tokenValues['foreign']);
    if ((int)$cross['status'] === 200
        || (int)($cross['data']['data']['log_id'] ?? 0)
            === (int)$journal['log_id']) {
        throw new RuntimeException('Foreign Gift detail not rejected');
    }
    [$anonymous] = $request('');
    if ((int)$anonymous['status'] === 200) {
        throw new RuntimeException('Anonymous Gift detail accepted');
    }
    $stage = 'independent_source_after_get';
    $connect = static function (string $prefix) use ($config, $dialect): PDO {
        $dsn = $dialect === 'dm'
            ? 'dm:host=' . $config[$prefix . 'HOST'] . ';port='
                . $config[$prefix . 'PORT'] . ';schema=' . $config[$prefix . 'SCHEMA']
            : 'mysql:host=' . $config[$prefix . 'HOST'] . ';port='
                . $config[$prefix . 'PORT'] . ';dbname='
                . $config[$prefix . 'SCHEMA'] . ';charset=utf8';
        return new PDO($dsn, $config[$prefix . 'USER'], $config[$prefix . 'PASSWORD'],
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_CASE => PDO::CASE_LOWER, PDO::ATTR_TIMEOUT => 5]);
    };
    $newAi = $connect('POC_AI_');
    $newStore = $connect('POC_');
    if (!$strictIdentity($newAi, 'ai') || !$strictIdentity($newStore, 'store')) {
        throw new RuntimeException('Gift detail post-HTTP customer DB identity drift');
    }
    $ownerAfter = $one($newAi, 'SELECT * FROM ' . $userTable
        . ' WHERE USER_ID=? AND NICK_NAME=? AND STORE_ID=10001 AND IS_DELETE=0',
        [$ownerId, $ownerNick]);
    $foreignAfter = $one($newAi, 'SELECT * FROM ' . $userTable
        . ' WHERE USER_ID=? AND NICK_NAME=? AND STORE_ID=10001 AND IS_DELETE=0',
        [$foreignId, $foreignNick]);
    $ownerStoreAfter = $one($newStore, 'SELECT * FROM ' . $storeUser
        . ' WHERE AI_USER_ID=? AND IS_DELETE=0', [$ownerId]);
    $foreignStoreAfter = $one($newStore, 'SELECT * FROM ' . $storeUser
        . ' WHERE AI_USER_ID=? AND IS_DELETE=0', [$foreignId]);
    $logAfter = $one($newAi, 'SELECT * FROM ' . $giftTable
        . ' WHERE LOG_ID=? AND USER_ID=? AND IDEMPOTENCY_KEY=? AND STORE_ID=10001',
        [(int)$journal['log_id'], $ownerId, $marker . ':O']);
    if ($digest($ownerAfter) !== (string)$journal['owner_user_sha']
        || $digest($foreignAfter) !== (string)$journal['foreign_user_sha']
        || $digest($ownerStoreAfter) !== (string)$journal['owner_store_sha']
        || $digest($foreignStoreAfter) !== (string)$journal['foreign_store_sha']
        || $digest($logAfter) !== (string)$journal['log_sha']) {
        throw new RuntimeException('Gift detail source changed after GET');
    }
    $journal['http_states'][$lane] = 'passed';
    if ($lane === 'A') $journal['contracts']['A'] = ['lane' => 'A', 'detail' => $contract];
    $writeJournal();
} catch (Throwable $error) {
    $failure = get_class($error) . ':' . hash('sha256', $error->getMessage());
    if ($requestComplete && ($journal['http_states'][$lane] ?? '') !== 'unknown') {
        $journal['http_states'][$lane] = 'confirmed_failure';
    }
    try { $writeJournal(); } catch (Throwable $ignored) { /* Retain prior journal. */ }
} finally {
    $stage = 'short_token_cleanup';
    try {
        foreach ($tokenValues as $token) \think\facade\Cache::delete($token);
        $tokensDeleted = true;
        $journal['token_states'][$lane] = 'deleted_needs_independent_zero';
        $writeJournal();
    } catch (Throwable $error) {
        $failure .= ($failure === '' ? '' : ';') . 'token_cleanup:'
            . get_class($error) . ':' . hash('sha256', $error->getMessage());
    }
}
echo json_encode(['status' => $failure === '' ? 'gift_detail_http_confirmed'
        : 'gift_detail_http_failed',
    'lane' => $lane, 'requests' => $requests,
    'detail_contract' => $contract,
    'token_deleted_first_process' => $tokensDeleted,
    'token_independent_zero_pending' => true,
    'source_unchanged' => $failure === '',
    'journal' => $journalPath, 'failure' => $failure,
    'db_dml' => 0], JSON_UNESCAPED_SLASHES), PHP_EOL;
if ($failure !== '' || count($requests) !== 3 || !$tokensDeleted) exit(1);
