<?php

declare(strict_types=1);

// Bounded, isolated GiftLog month-window verification. The default is read-only.
ini_set('display_errors', '0');
$stage = 'entry';
set_exception_handler(static function (Throwable $error) use (&$stage): void {
    echo json_encode(['status' => 'sanitized_error', 'stage' => $stage,
        'class' => get_class($error), 'code' => (string)$error->getCode(),
        'reason_sha' => hash('sha256', $error->getMessage())]), PHP_EOL;
    exit(2);
});

$lane = (string)getenv('CDX_GIFT_LIST_LANE');
$mode = (string)($argv[1] ?? '');
if (!in_array($lane, ['B', 'C'], true)
    || !in_array($mode, ['--preflight', '--prepare', '--read', '--token-zero',
        '--cleanup', '--abort-cleanup'], true)
    || ($mode !== '--preflight'
        && ((string)getenv('CDX_GIFT_LIST_APPROVED') !== 'GIFT_LIST_ISOLATED_V1'
            || (string)getenv('CDX_GIFT_LIST_SHA') !== hash_file('sha256', __FILE__)))) {
    echo json_encode(['status' => 'default_deny', 'http' => 0, 'dml' => 0]), PHP_EOL;
    exit(78);
}
$runtime = [
    'B' => '/private/tmp/zhct-http-ai-macos.x2CCZX',
    'C' => '/tmp/zhct-ai-http-20260929.3XDj3w',
][$lane];
$source = [
    'B' => '/Users/wiscourper/Documents/workspace/local/dm8-full-acceptance.fF5yTy/ai_api',
    'C' => '/tmp/zhct-dm-poc-20260928.fcpiiN/ai_api',
][$lane];
$port = ['B' => 18101, 'C' => 18102][$lane];
$stage = 'isolated_http_boundary';
$boundaryContext = stream_context_create(['http' => [
    'method' => 'GET', 'timeout' => 3, 'ignore_errors' => true,
    'follow_location' => 0, 'max_redirects' => 0,
]]);
$boundaryRaw = @file_get_contents('http://127.0.0.1:' . $port
    . '/__poc_boundary', false, $boundaryContext);
$boundary = is_string($boundaryRaw) ? json_decode($boundaryRaw, true) : null;
if (!is_array($boundary) || empty($boundary['external_stubs'])) {
    throw new RuntimeException('Gift isolated HTTP external stubs unavailable');
}
$expectedModel = 'fb7c567c2d77998714133a4aae9012f94421b75ba79ebe3c30833389a3d12d89';
$modelPath = $source . '/app/api/model/user/GiftLog.php';
if (!is_file($modelPath) || is_link($modelPath)
    || hash_file('sha256', $modelPath) !== $expectedModel) {
    throw new RuntimeException('Actual GiftLog model source drift');
}
foreach ([
    'app/api/controller/gift/Log.php'
        => '9d70fbc0555f77697065ddb6568cf85cdc9cfdd7b6c69589e4fb617cdf592c4d',
    'app/api/service/gift/Log.php'
        => '03c3af93dc4a08c0932ee573112dfe5ac9ad7e225c902ca18f0cb9de31df4681',
] as $relative => $expectedSha) {
    $path = $source . '/' . $relative;
    if (!is_file($path) || is_link($path)
        || hash_file('sha256', $path) !== $expectedSha) {
        throw new RuntimeException('Gift route/service source drift');
    }
}
$helper = $source . '/app/common/service/FixedScaleDecimalResponse.php';
if (!is_file($helper) || is_link($helper)
    || hash_file('sha256', $helper)
        !== 'c3bce04654a313e2918667a8a1019eb7b4c45b98edf9a5218d3e8c40b8d333cc') {
    throw new RuntimeException('Gift decimal helper source drift');
}

$identity = $lane === 'C'
    ? '/tmp/zhct-ai-read2-VgKO4CDY/read2_schema_preflight.php'
    : __DIR__ . '/../parallel-ai-next-read-safe4/read2_schema_preflight.php';
if (!is_file($identity) || is_link($identity)
    || hash_file('sha256', $identity)
        !== '8641ee015ad537d300de9c00f2c882e654b739d7c2194dd13ef4635ca9df1af0') {
    throw new RuntimeException('Approved two-database identity source drift');
}
$stage = 'two_database_identity';
putenv('CDX_READ2_TARGET=' . $lane);
putenv('CDX_READ2_SCHEMA_PREFLIGHT=1');
require $identity; // Defines already-verified $ai, $store, $dialect and $config.
$ai->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
$store->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);

$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';
$ownerNick = $lane === 'C' ? 'CDXPARMOB9411C2CB' : 'CDXPARMOB426CA4B0';
$foreignNick = $lane === 'C' ? 'CDXPAY661BEE6B' : 'CDXPAY112FC189';
$markerPrefix = 'CDXGL6';
$one = static function (PDO $db, string $sql, array $params = []): array {
    $statement = $db->prepare($sql);
    $statement->execute($params);
    $rows = $statement->fetchAll(PDO::FETCH_ASSOC);
    if (count($rows) !== 1) throw new RuntimeException('Expected exactly one owned row');
    $row = [];
    foreach (array_change_key_case($rows[0], CASE_LOWER) as $key => $value) {
        $row[$key] = is_resource($value) ? stream_get_contents($value) : $value;
    }
    return $row;
};
$count = static function (PDO $db, string $sql, array $params = []): int {
    $statement = $db->prepare($sql);
    $statement->execute($params);
    return (int)$statement->fetchColumn();
};
$digest = static function (array $row): string {
    ksort($row, SORT_STRING);
    return hash('sha256', serialize($row));
};
$identityOk = static function (PDO $db, string $side) use ($dialect): bool {
    if ($dialect === 'dm') {
        $expected = $side === 'ai' ? 'AIZHCT_APP' : 'ZHCT_APP';
        return strtoupper((string)$db->query('SELECT USER FROM DUAL')->fetchColumn()) === $expected
            && strtoupper((string)$db->query(
                "SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn()) === $expected;
    }
    $expectedSha = $side === 'ai'
        ? '210f925e093f71e26a8c8923f0f626e3be0a15ccff3d386bba21feef695c8a5b'
        : '003eb5aecf1cd4595b609c1f7f20b9ee325b578309fb9fe14fedaf77584fa401';
    $identity = (string)$db->query(
        "SELECT CONCAT(@@server_uuid,'|',@@hostname,'|',@@port,'|',DATABASE())"
    )->fetchColumn();
    return hash_equals($expectedSha, hash('sha256', $identity));
};
if (!$identityOk($ai, 'ai') || !$identityOk($store, 'store')) {
    throw new RuntimeException('Gift target identity drift');
}
$stage = 'schema_and_actor_preflight';
$meta = $dialect === 'dm'
    ? $one($ai, 'SELECT DATA_TYPE FROM USER_TAB_COLUMNS WHERE TABLE_NAME=? AND COLUMN_NAME=?',
        [$giftTable, 'CREATE_TIME'])
    : $one($ai, 'SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS'
        . ' WHERE TABLE_SCHEMA=? AND TABLE_NAME=? AND COLUMN_NAME=?',
        [(string)$config['POC_AI_SCHEMA'], $giftTable, 'create_time']);
$rawType = strtoupper((string)$meta['data_type']);
if (($dialect === 'dm' && $rawType !== 'DATETIME')
    || ($dialect === 'mysql' && $rawType !== 'INT')) {
    throw new RuntimeException('Gift CREATE_TIME physical type drift');
}
$giftTriggers = $dialect === 'dm'
    ? $count($ai, 'SELECT COUNT(*) FROM USER_TRIGGERS WHERE TABLE_NAME=?', [$giftTable])
    : $count($ai, 'SELECT COUNT(*) FROM INFORMATION_SCHEMA.TRIGGERS'
        . ' WHERE TRIGGER_SCHEMA=? AND EVENT_OBJECT_TABLE=?',
        [(string)$config['POC_AI_SCHEMA'], $giftTable]);
$giftFk = $dialect === 'dm'
    ? $count($ai, "SELECT COUNT(*) FROM USER_CONSTRAINTS"
        . " WHERE TABLE_NAME=? AND CONSTRAINT_TYPE='R'", [$giftTable])
    : $count($ai, 'SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE'
        . ' WHERE TABLE_SCHEMA=? AND TABLE_NAME=?'
        . ' AND REFERENCED_TABLE_NAME IS NOT NULL',
        [(string)$config['POC_AI_SCHEMA'], $giftTable]);
if ($giftTriggers !== 0 || $giftFk !== 0) {
    throw new RuntimeException('Gift fixture trigger/FK boundary drift');
}
$owner = $one($ai, 'SELECT * FROM ' . $userTable
    . ' WHERE NICK_NAME=? AND STORE_ID=10001 AND IS_DELETE=0', [$ownerNick]);
$foreign = $one($ai, 'SELECT * FROM ' . $userTable
    . ' WHERE NICK_NAME=? AND STORE_ID=10001 AND IS_DELETE=0', [$foreignNick]);
$ownerId = (int)$owner['user_id'];
$foreignId = (int)$foreign['user_id'];
if ($ownerId <= 0 || $foreignId <= 0 || $ownerId === $foreignId) {
    throw new RuntimeException('Gift owned actor identity drift');
}
$ownerStore = $one($store, 'SELECT * FROM ' . $storeUser
    . ' WHERE AI_USER_ID=? AND IS_DELETE=0', [$ownerId]);
$foreignStore = $one($store, 'SELECT * FROM ' . $storeUser
    . ' WHERE AI_USER_ID=? AND IS_DELETE=0', [$foreignId]);
$prefixCount = $count($ai, 'SELECT COUNT(*) FROM ' . $giftTable
    . ' WHERE IDEMPOTENCY_KEY LIKE ?', [$markerPrefix . '%']);
$sourceHash = [
    'owner' => $digest($owner), 'foreign' => $digest($foreign),
    'owner_store' => $digest($ownerStore), 'foreign_store' => $digest($foreignStore),
];

foreach ($config as $name => $value) {
    if (strpos($name, 'POC_') === 0) putenv($name . '=' . $value);
}
define('TP_ENV', 'poc');
define('BUSINESS', 'aizhct');
define('ENTERPRISE_NO', '10001');
require $runtime . '/vendor/autoload.php';
if ((new ReflectionClass(\app\api\model\user\GiftLog::class))->getFileName() !== $modelPath) {
    throw new RuntimeException('Composer resolved unexpected GiftLog source');
}
(new \think\App($runtime))->setEnvName('poc')->initialize();
$start = microtime(true);
$ormType = (string)(new \app\api\model\user\GiftLog())->db()->getFieldType('create_time');
$metadataMs = round((microtime(true) - $start) * 1000, 3);
if ($ormType !== ($dialect === 'dm' ? 'datetime' : 'int')
    || date_default_timezone_get() !== 'Asia/Shanghai'
    || !function_exists('bccomp')) {
    throw new RuntimeException('Actual ORM metadata/timezone drift');
}
$cacheRoot = realpath($runtime . '/runtime/cache');
if ($cacheRoot !== $runtime . '/runtime/cache'
    || (((int)fileperms($runtime) & 0077) !== 0)
    || 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 private File cache boundary drift');
}
if ($mode === '--preflight') {
    if ($prefixCount !== 0 || $count($ai, 'SELECT COUNT(*) FROM ' . $giftTable
        . ' WHERE USER_ID=?', [$ownerId]) !== 0) {
        throw new RuntimeException('Owned gift source not empty before fixture');
    }
    echo json_encode(['status' => 'preflight_pass', 'lane' => $lane,
        'raw_type' => $rawType, 'orm_type' => $ormType,
        'metadata_ms' => $metadataMs, 'model_sha' => $expectedModel,
        'marker_rows' => 0, 'owned_gift_rows' => 0,
        'gift_fk' => 0, 'gift_triggers' => 0,
        'business_http' => 0, 'dml' => 0], JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(0);
}

$period = [
    'S' => '2026-09-15 12:00:00',
    'N' => '2026-10-01 00:00:00',
];
$zone = new DateTimeZone('Asia/Shanghai');
$epoch = [];
foreach ($period as $key => $date) {
    $epoch[$key] = (new DateTimeImmutable($date, $zone))->getTimestamp();
}
$newAi = static function () use ($config, $dialect): PDO {
    $dsn = $dialect === 'dm'
        ? 'dm:host=127.0.0.1;port=5238;schema=AIZHCT_APP'
        : 'mysql:host=' . $config['POC_AI_HOST'] . ';port=' . $config['POC_AI_PORT']
            . ';dbname=' . $config['POC_AI_SCHEMA'] . ';charset=utf8';
    return new PDO($dsn, $config['POC_AI_USER'], $config['POC_AI_PASSWORD'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_CASE => PDO::CASE_LOWER, PDO::ATTR_TIMEOUT => 5]);
};
$newStore = static function () use ($config, $dialect): PDO {
    $dsn = $dialect === 'dm'
        ? 'dm:host=127.0.0.1;port=5237;schema=ZHCT_APP'
        : 'mysql:host=' . $config['POC_HOST'] . ';port=' . $config['POC_PORT']
            . ';dbname=' . $config['POC_SCHEMA'] . ';charset=utf8';
    return new PDO($dsn, $config['POC_USER'], $config['POC_PASSWORD'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_CASE => PDO::CASE_LOWER, PDO::ATTR_TIMEOUT => 5]);
};
$journalPath = (string)getenv('CDX_GIFT_LIST_JOURNAL');
$journalDir = dirname($journalPath);
$writeJournal = static function (array $journal) use (&$journalPath, &$journalDir): void {
    $temporary = tempnam($journalDir, 'next-');
    if (!is_string($temporary) || !chmod($temporary, 0600)) {
        throw new RuntimeException('Private gift journal temp unavailable');
    }
    $json = json_encode($journal, JSON_UNESCAPED_SLASHES);
    if (!is_string($json) || file_put_contents($temporary, $json, LOCK_EX) !== strlen($json)
        || !rename($temporary, $journalPath)) {
        throw new RuntimeException('Private gift journal atomic write failed');
    }
};
if ($mode === '--prepare') {
    if ($journalPath !== '' || $prefixCount !== 0
        || $count($ai, 'SELECT COUNT(*) FROM ' . $giftTable . ' WHERE USER_ID=?', [$ownerId]) !== 0) {
        throw new RuntimeException('Gift fixture not empty or unexpected journal supplied');
    }
    $marker = $markerPrefix . strtoupper(bin2hex(random_bytes(6)));
    $journalDir = sys_get_temp_dir() . '/cdx-gift-list-' . $marker;
    if (file_exists($journalDir) || !mkdir($journalDir, 0700)) {
        throw new RuntimeException('Private gift journal directory unavailable');
    }
    chmod($journalDir, 0700);
    $journalPath = $journalDir . '/fixture.json';
    $journal = ['lane_group' => $lane, 'marker' => $marker,
        'owner_id' => $ownerId, 'foreign_id' => $foreignId,
        'source_hash' => $sourceHash, 'row_ids' => [], 'row_hash' => [],
        'http_states' => [], 'token_states' => [], 'tokens' => [],
        'contracts' => [], 'state' => 'before_insert'];
    $writeJournal($journal);
    $stage = 'insert_two_owned_logs';
    $ai->beginTransaction();
    try {
        $describe = $dialect === 'dm' ? '"DESCRIBE"' : '`describe`';
        $sql = 'INSERT INTO ' . $giftTable
            . ' (USER_ID,SCENE,MONEY,BALANCE_BEFORE,BALANCE_AFTER,BIZ_TYPE,'
            . ' BIZ_ORDER_ID,BIZ_ORDER_NO,IDEMPOTENCY_KEY,' . $describe . ',REMARK,'
            . ' OPERATOR_ID,OPERATOR_NAME,STORE_ID,CREATE_TIME)'
            . ' VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,10001,?)';
        foreach (['S', 'N'] as $key) {
            $boundDate = $dialect === 'dm' ? $period[$key] : $epoch[$key];
            $ai->prepare($sql)->execute([$ownerId, $key === 'S' ? 20 : 10,
                $key === 'S' ? '-0.01' : '0.01',
                $key === 'S' ? '200.00' : '199.99',
                $key === 'S' ? '199.99' : '200.00',
                'consume', 0, $marker . $key, $marker . ':' . $key,
                'Codex gift list compatibility fixture', $marker, $marker,
                'CODEX_TEST', $boundDate]);
            $row = $one($ai, 'SELECT * FROM ' . $giftTable
                . ' WHERE USER_ID=? AND STORE_ID=10001 AND IDEMPOTENCY_KEY=?',
                [$ownerId, $marker . ':' . $key]);
            if ((int)$row['log_id'] <= 0
                || ($dialect === 'dm'
                    ? substr((string)$row['create_time'], 0, 19) !== $period[$key]
                    : (string)$row['create_time'] !== (string)$epoch[$key])) {
                throw new RuntimeException('Gift physical date insert/read differs');
            }
            $journal['row_ids'][$key] = (int)$row['log_id'];
            $journal['row_hash'][$key] = $digest($row);
        }
        $ai->commit();
    } catch (Throwable $error) {
        if ($ai->inTransaction()) $ai->rollBack();
        $journal['state'] = 'requires_independent_audit_after_insert_error';
        $writeJournal($journal);
        throw $error;
    }
    $journal['state'] = 'prepared_two_rows';
    $writeJournal($journal);
    echo json_encode(['status' => 'prepared', 'lane_group' => $journal['lane_group'],
        'journal' => $journalPath, 'marker_sha12' => substr(hash('sha256', $marker), 0, 12),
        'owned_gift_rows' => 2, 'other_business_rows_written' => 0,
        'business_http' => 0], JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(0);
}

if (basename($journalPath) !== 'fixture.json'
    || !preg_match('/^cdx-gift-list-CDXGL6[0-9A-F]{12}$/', basename($journalDir))
    || !is_dir($journalDir) || is_link($journalDir)
    || !is_file($journalPath) || is_link($journalPath)
    || (((int)fileperms($journalDir) & 0077) !== 0)
    || (((int)fileperms($journalPath) & 0077) !== 0)) {
    throw new RuntimeException('Exact private gift journal unavailable');
}
$journal = json_decode((string)file_get_contents($journalPath), true);
if (!is_array($journal)
    || ($journal['lane_group'] ?? '') !== $lane
    || !preg_match('/^CDXGL6[0-9A-F]{12}$/', (string)($journal['marker'] ?? ''))
    || (int)($journal['owner_id'] ?? 0) !== $ownerId
    || (int)($journal['foreign_id'] ?? 0) !== $foreignId
    || count($journal['row_ids'] ?? []) !== 2
    || $prefixCount !== 2
    || $sourceHash !== ($journal['source_hash'] ?? null)) {
    throw new RuntimeException('Gift owned journal/source drift');
}
$rows = [];
foreach (['S', 'N'] as $key) {
    $rows[$key] = $one($ai, 'SELECT * FROM ' . $giftTable
        . ' WHERE LOG_ID=? AND USER_ID=? AND STORE_ID=10001 AND IDEMPOTENCY_KEY=?',
        [(int)$journal['row_ids'][$key], $ownerId, $journal['marker'] . ':' . $key]);
    if ($digest($rows[$key]) !== $journal['row_hash'][$key]) {
        throw new RuntimeException('Gift source log drift');
    }
}
if ($mode === '--token-zero') {
    if (($journal['token_states'][$lane] ?? '') !== 'deleted_needs_independent_zero'
        || count($journal['tokens'][$lane] ?? []) !== 2) {
        throw new RuntimeException('Two deleted token pointers required');
    }
    foreach ($journal['tokens'][$lane] as $token) {
        if (!is_string($token) || strlen($token) < 20
            || \think\facade\Cache::has($token)) {
            throw new RuntimeException('Gift short token still present');
        }
    }
    unset($journal['tokens'][$lane]);
    $journal['token_states'][$lane] = 'zero';
    $writeJournal($journal);
    echo json_encode(['status' => 'independent_token_zero', 'lane' => $lane,
        'token_count' => 2, 'business_http' => 0, 'dml' => 0]), PHP_EOL;
    exit(0);
}

if ($mode === '--cleanup' || $mode === '--abort-cleanup') {
    if ($mode === '--abort-cleanup'
        && (string)getenv('CDX_GIFT_LIST_ABORT_APPROVED') !== 'KNOWN_HTTP_ABORT_EXACT_ZERO_V1') {
        throw new RuntimeException('Separate known-abort cleanup approval required');
    }
    $required = [$lane];
    if ($required === []) throw new RuntimeException('No attempted Gift lane for abort cleanup');
    foreach ($required as $target) {
        $state = $journal['http_states'][$target] ?? '';
        $tokenState = $journal['token_states'][$target] ?? '';
        $knownNeverStarted = $state === '' && $tokenState === ''
            && !isset($journal['tokens'][$target]);
        if (($mode === '--cleanup' && ($state !== 'confirmed' || $tokenState !== 'zero'))
            || ($mode === '--abort-cleanup' && !$knownNeverStarted
                && (!in_array($state, ['confirmed', 'confirmed_failure'], true)
                    || $tokenState !== 'zero'))) {
            throw new RuntimeException('Gift cleanup requires confirmed HTTP and token zero');
        }
    }
    $stage = 'exact_two_row_cleanup';
    $ai->beginTransaction();
    try {
        foreach (['S', 'N'] as $key) {
            $statement = $ai->prepare('DELETE FROM ' . $giftTable
                . ' WHERE LOG_ID=? AND USER_ID=? AND STORE_ID=10001 AND IDEMPOTENCY_KEY=?');
            $statement->execute([(int)$journal['row_ids'][$key], $ownerId,
                $journal['marker'] . ':' . $key]);
            if ($statement->rowCount() !== 1) {
                throw new RuntimeException('Gift exact delete affected-row mismatch');
            }
        }
        $ai->commit();
    } catch (Throwable $error) {
        if ($ai->inTransaction()) $ai->rollBack();
        throw $error;
    }
    $checkAi = $newAi();
    $checkStore = $newStore();
    if (!$identityOk($checkAi, 'ai') || !$identityOk($checkStore, 'store')
        || $count($checkAi, 'SELECT COUNT(*) FROM ' . $giftTable
            . ' WHERE IDEMPOTENCY_KEY LIKE ?', [$journal['marker'] . '%']) !== 0
        || $count($checkAi, 'SELECT COUNT(*) FROM ' . $giftTable
            . ' WHERE USER_ID=?', [$ownerId]) !== 0) {
        throw new RuntimeException('Gift independent new-PDO row zero failed');
    }
    $after = [
        'owner' => $digest($one($checkAi, 'SELECT * FROM ' . $userTable
            . ' WHERE USER_ID=? AND NICK_NAME=? AND STORE_ID=10001 AND IS_DELETE=0',
            [$ownerId, $ownerNick])),
        'foreign' => $digest($one($checkAi, 'SELECT * FROM ' . $userTable
            . ' WHERE USER_ID=? AND NICK_NAME=? AND STORE_ID=10001 AND IS_DELETE=0',
            [$foreignId, $foreignNick])),
        'owner_store' => $digest($one($checkStore, 'SELECT * FROM ' . $storeUser
            . ' WHERE AI_USER_ID=? AND IS_DELETE=0', [$ownerId])),
        'foreign_store' => $digest($one($checkStore, 'SELECT * FROM ' . $storeUser
            . ' WHERE AI_USER_ID=? AND IS_DELETE=0', [$foreignId])),
    ];
    if ($after !== $journal['source_hash'] || !unlink($journalPath) || !rmdir($journalDir)) {
        throw new RuntimeException('Gift independent user/source/journal zero failed');
    }
    echo json_encode(['status' => 'exact_cleanup_pass', 'lane_group' => $journal['lane_group'],
        'deleted_gift_rows' => 2, 'independent_marker_zero' => true,
        'user_and_store_source_unchanged' => true, 'journal_removed' => true]), PHP_EOL;
    exit(0);
}

if (($journal['http_states'][$lane] ?? '') !== ''
    || isset($journal['tokens'][$lane]) || isset($journal['token_states'][$lane])) {
    throw new RuntimeException('Gift lane already attempted, no blind retry');
}
$stage = 'business_http';
$tokens = [];
$httpMs = [];
$contracts = [];
$failure = '';
$normal = static function (array $response): array {
    unset($response['trace_id']);
    return $response;
};
$shape = null;
$shape = static function ($value) use (&$shape) {
    if (is_array($value)) {
        if (array_keys($value) === range(0, count($value) - 1)) {
            return ['array' => array_map($shape, $value)];
        }
        ksort($value, SORT_STRING);
        return ['object' => array_map($shape, $value)];
    }
    return gettype($value);
};
$request = static function (string $path, array $params, string $token)
    use ($port, $lane, &$journal, $writeJournal, &$httpMs): array {
    if (!in_array($path, ['/api/gift.Log/list', '/api/gift.Log/detail'], true)) {
        throw new RuntimeException('Gift HTTP route outside whitelist');
    }
    $params = array_merge(['storeId' => 10001], $params);
    $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', 'header' => $headers, 'timeout' => 15,
        'ignore_errors' => true, 'follow_location' => 0, 'max_redirects' => 0,
    ]]);
    $journal['http_states'][$lane] = 'inflight';
    $writeJournal($journal);
    $started = microtime(true);
    $stream = @fopen('http://127.0.0.1:' . $port . $path . '?'
        . http_build_query($params, '', '&', PHP_QUERY_RFC3986), '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);
    $elapsed = round((microtime(true) - $started) * 1000, 1);
    if (!is_string($body) || !empty($meta['timed_out']) || strlen($body) > 65536) {
        $journal['http_states'][$lane] = 'unknown';
        $writeJournal($journal);
        throw new RuntimeException('Gift HTTP transport outcome unknown or oversized');
    }
    $headersOut = $http_response_header ?? [];
    $httpCode = preg_match('/HTTP\/\S+\s+(\d+)/', (string)($headersOut[0] ?? ''), $match)
        ? (int)$match[1] : 0;
    $json = json_decode($body, true);
    if ($httpCode !== 200 || !is_array($json) || !array_key_exists('status', $json)) {
        $journal['http_states'][$lane] = 'unknown';
        $writeJournal($journal);
        throw new RuntimeException('Gift HTTP response not confirmed business JSON');
    }
    $journal['http_states'][$lane] = 'confirmed';
    $writeJournal($journal);
    $httpMs[] = ['path' => $path, 'ms' => $elapsed, 'business_status' => (int)$json['status']];
    return $json;
};
$rowCheck = static function (array $item, array $sourceRow, string $dateExpected,
    bool $detail): void {
    if ((int)($item['log_id'] ?? 0) !== (int)$sourceRow['log_id']
        || (int)($item['user_id'] ?? 0) !== (int)$sourceRow['user_id']
        || (int)($item['scene'] ?? 0) !== (int)$sourceRow['scene']
        || (string)($item['create_time'] ?? '') !== $dateExpected) {
        throw new RuntimeException('Gift HTTP identity/date not sourced from own log');
    }
    foreach (['money', 'balance_before', 'balance_after'] as $field) {
        if (!is_string($item[$field] ?? null)
            || !preg_match('/^-?\d+\.\d{2}$/', $item[$field])
            || bccomp($item[$field], (string)$sourceRow[$field], 2) !== 0) {
            throw new RuntimeException('Gift HTTP amount type or cents mismatch');
        }
    }
    foreach (['store_id', 'idempotency_key', 'is_delete', 'create_by', 'update_time'] as $field) {
        if (array_key_exists($field, $item)) {
            throw new RuntimeException('Gift internal field leaked');
        }
    }
    if ($detail && ((string)($item['order_no'] ?? '') !== (string)$sourceRow['biz_order_no']
        || (string)($item['pay_method'] ?? '__MISSING__') !== '')) {
        throw new RuntimeException('Gift derived detail fields mismatch');
    }
};
try {
    $stage = 'issue_short_tokens';
    foreach (['owner' => $ownerId, 'foreign' => $foreignId] as $role => $id) {
        $token = 'CDXGL6T' . strtoupper(bin2hex(random_bytes(16)));
        $tokens[$role] = $token;
        $journal['tokens'][$lane][$role] = $token;
    }
    $journal['token_states'][$lane] = 'issued';
    $writeJournal($journal);
    foreach ($tokens as $role => $token) {
        $id = $role === 'owner' ? $ownerId : $foreignId;
        $authUser = \app\api\model\User::detail($id);
        if (empty($authUser) || (int)$authUser['user_id'] !== $id) {
            throw new RuntimeException('Gift owned authentication actor unavailable');
        }
        $mapping = $role === 'owner' ? $ownerStore : $foreignStore;
        if (!\think\facade\Cache::set($token, ['user' => $authUser,
                'store_id' => 10001, 'is_login' => true,
                'password_version' => (int)$mapping['password_version'],
                'login_type' => 'sms'], 180)
            || !\think\facade\Cache::has($token)) {
            throw new RuntimeException('Gift private short token unavailable');
        }
    }
    $stage = 'business_http';
    $sep = $request('/api/gift.Log/list', ['year' => 2026, 'month' => 9,
        'scene' => 0, 'page' => 1], $tokens['owner']);
    if ((int)$sep['status'] !== 200) throw new RuntimeException('Gift September list failed');
    $sepList = $sep['data']['list'] ?? null;
    $sepRows = is_array($sepList) ? ($sepList['data'] ?? null) : null;
    if (!is_array($sepRows)) throw new RuntimeException('Gift September list envelope absent');
    if (count($sepRows) !== 1 || (int)($sepList['total'] ?? -1) !== 1) {
        throw new RuntimeException('Gift September month window should contain one log');
    }
    $rowCheck($sepRows[0], $rows['S'], $period['S'], false);
    $contracts['sep'] = ['sha' => hash('sha256', json_encode($normal($sep))),
        'shape' => $shape($normal($sep))];
    {
        $oct = $request('/api/gift.Log/list', ['year' => 2026, 'month' => 10,
            'scene' => 0, 'page' => 1], $tokens['owner']);
        $octList = $oct['data']['list'] ?? null;
        $octRows = is_array($octList) ? ($octList['data'] ?? null) : null;
        if ((int)$oct['status'] !== 200 || !is_array($octRows)
            || count($octRows) !== 1 || (int)($octList['total'] ?? -1) !== 1) {
            throw new RuntimeException('Gift next-month boundary should contain one log');
        }
        $rowCheck($octRows[0], $rows['N'], $period['N'], false);
        $contracts['oct'] = ['sha' => hash('sha256', json_encode($normal($oct))),
            'shape' => $shape($normal($oct))];
        $scene = $request('/api/gift.Log/list', ['year' => 2026, 'month' => 9,
            'scene' => 20, 'page' => 1], $tokens['owner']);
        $sceneList = $scene['data']['list'] ?? null;
        $sceneRows = is_array($sceneList) ? ($sceneList['data'] ?? null) : null;
        if ((int)$scene['status'] !== 200 || !is_array($sceneRows)
            || count($sceneRows) !== 1) {
            throw new RuntimeException('Gift scene filter positive mismatch');
        }
        $rowCheck($sceneRows[0], $rows['S'], $period['S'], false);
        $contracts['scene'] = ['sha' => hash('sha256', json_encode($normal($scene))),
            'shape' => $shape($normal($scene))];
    }
    $detail = $request('/api/gift.Log/detail', ['logId' => $journal['row_ids']['S'],
        'log_id' => $journal['row_ids']['S']], $tokens['owner']);
    $detailRow = $detail['data']['data'] ?? null;
    if ((int)$detail['status'] !== 200 || !is_array($detailRow)) {
        throw new RuntimeException('Gift own detail failed');
    }
    $rowCheck($detailRow, $rows['S'], $period['S'], true);
    $contracts['detail'] = ['sha' => hash('sha256', json_encode($normal($detail))),
        'shape' => $shape($normal($detail))];
    $foreignDetail = $request('/api/gift.Log/detail',
        ['logId' => $journal['row_ids']['S'], 'log_id' => $journal['row_ids']['S']],
        $tokens['foreign']);
    if ((int)$foreignDetail['status'] === 200
        || (int)($foreignDetail['data']['data']['log_id'] ?? 0) === (int)$journal['row_ids']['S']) {
        throw new RuntimeException('Gift foreign actor read owner detail');
    }
    $withoutAuth = $request('/api/gift.Log/list', ['year' => 2026, 'month' => 9], '');
    if ((int)$withoutAuth['status'] === 200) {
        throw new RuntimeException('Gift unauthenticated list accepted');
    }
    $stage = 'post_http_source_readback';
    $checkAi = $newAi();
    $checkStore = $newStore();
    if (!$identityOk($checkAi, 'ai') || !$identityOk($checkStore, 'store')) {
        throw new RuntimeException('Gift post-HTTP independent target identity drift');
    }
    foreach (['S', 'N'] as $key) {
        $post = $one($checkAi, 'SELECT * FROM ' . $giftTable
            . ' WHERE LOG_ID=? AND USER_ID=? AND IDEMPOTENCY_KEY=? AND STORE_ID=10001',
            [(int)$journal['row_ids'][$key], $ownerId, $journal['marker'] . ':' . $key]);
        if ($digest($post) !== $journal['row_hash'][$key]) {
            throw new RuntimeException('Gift source log changed after GET');
        }
    }
    $journal['http_states'][$lane] = 'confirmed';
    $journal['contracts'][$lane] = $contracts;
    $writeJournal($journal);
} catch (Throwable $error) {
    $failure = get_class($error) . ':' . hash('sha256', $error->getMessage());
    if (($journal['http_states'][$lane] ?? '') !== 'unknown'
        && ($journal['http_states'][$lane] ?? '') !== 'inflight') {
        $journal['http_states'][$lane] = 'confirmed_failure';
    }
    try { $writeJournal($journal); } catch (Throwable $ignored) { /* Recovery pointer remains. */ }
} finally {
    $stage = 'short_token_cleanup';
    try {
        foreach ($tokens as $token) \think\facade\Cache::delete($token);
        $journal['token_states'][$lane] = 'deleted_needs_independent_zero';
        $writeJournal($journal);
    } catch (Throwable $error) {
        $failure .= ($failure === '' ? '' : ';') . 'token_cleanup:'
            . get_class($error) . ':' . hash('sha256', $error->getMessage());
    }
}
echo json_encode(['status' => $failure === '' ? 'http_confirmed' : 'http_failed',
    'lane' => $lane,
    'contracts' => $contracts, 'http_ms' => $httpMs,
    'source_unchanged' => $failure === '', 'journal' => $journalPath,
    'failure' => $failure, 'independent_token_zero_pending' => true,
    'db_dml' => 0], JSON_UNESCAPED_SLASHES), PHP_EOL;
if ($failure !== '') exit(1);
