<?php

declare(strict_types=1);

$root = getenv('POC_APP_ROOT');
$dialect = getenv('POC_DIALECT');
if (!$root || !in_array($dialect, ['mysql', 'dm'], true)) exit(2);
foreach (['HOST', 'PORT', 'SCHEMA', 'USER', 'PASSWORD',
    'AI_HOST', 'AI_PORT', 'AI_SCHEMA', 'AI_USER', 'AI_PASSWORD'] as $part) {
    if (!getenv('POC_' . $part)) exit(2);
}
define('APP_PATH', $root . '/application/');
require $root . '/thinkphp/base.php';
require $root . '/application/common.php';
if (!function_exists('config')) {
    function config($name = '', $value = null, $range = '')
    {
        return $value === null ? \think\Config::get($name) : \think\Config::set($name, $value, $range);
    }
}
set_exception_handler(static function (Throwable $error): void {
    fwrite(STDERR, 'FIXTURE_REHEARSAL_FAIL ' . get_class($error) . ': '
        . substr($error->getMessage(), 0, 240) . PHP_EOL);
    exit(1);
});
$config = [
    'type' => $dialect === 'dm' ? \app\cores\database\Dm::class : 'mysql',
    'charset' => $dialect === 'mysql' && getenv('POC_MYSQL_UTF8MB4') === '1'
        ? 'utf8mb4' : 'utf8',
    'hostname' => getenv('POC_HOST'),
    'hostport' => (int)getenv('POC_PORT'),
    'database' => getenv('POC_SCHEMA'),
    'username' => getenv('POC_USER'),
    'password' => getenv('POC_PASSWORD'),
    'prefix' => 'ydy_',
    'debug' => false,
    'resultset_type' => 'array',
    'fields_strict' => true,
    'ai_zhct' => [
        'hostname' => getenv('POC_AI_HOST'),
        'hostport' => (int)getenv('POC_AI_PORT'),
        'database' => getenv('POC_AI_SCHEMA'),
        'username' => getenv('POC_AI_USER'),
        'password' => getenv('POC_AI_PASSWORD'),
        'charset' => $dialect === 'mysql' && getenv('POC_MYSQL_UTF8MB4') === '1'
            ? 'utf8mb4' : 'utf8',
        'prefix' => 'yoshop_',
    ],
];
\think\Config::set($config, 'database');
\think\Db::clear();
$store = \think\Db::connect();
$ai = get_ai_db();
$marker = 'CDXPAY' . strtoupper(bin2hex(random_bytes(4)));
$staffUuid = 'CDXPAY-' . strtoupper(bin2hex(random_bytes(14)));
$userUuid = 'CDXUSER-' . strtoupper(bin2hex(random_bytes(13)));
$authPwd = strtoupper(bin2hex(random_bytes(8)));
$authKey = strtoupper(bin2hex(random_bytes(8)));
$now = date('Y-m-d H:i:s');
$storeStarted = false;
$aiStarted = false;
$authCommitted = false;
$expectCommitFault = false;
$adminRecharge = getenv('POC_ADMIN_RECHARGE') === '1';
$adminSubsidy = getenv('POC_ADMIN_SUBSIDY') === '1';
if ($adminRecharge && $adminSubsidy) {
    throw new RuntimeException('Choose one test funding mode');
}
$adminFunded = $adminRecharge || $adminSubsidy;
$fundedWallet = [];
try {
    $store->startTrans();
    $storeStarted = true;
    $ai->startTrans();
    $aiStarted = true;
    $aiId = (int)$ai->name('user')->insertGetId([
        'store_id' => 10001,
        'mobile' => '',
        'nick_name' => $marker,
        'balance' => $adminFunded ? '0.00' : '100.00',
        'gift_balance' => '0.00',
        'is_delete' => 0,
        'create_time' => time(),
    ]);
    $staffId = (int)$store->name('staff')->insertGetId([
        'uuid' => $staffUuid,
        'station_uuid' => 'CDXPAY-STATION',
        'name' => $marker,
        'birthday' => '1990-01-01',
        'sport_uuid' => 'CDXPAY-SPORT',
        'sport_event_uuid' => '[]',
        'in_time' => date('Y-m-d'),
        'create_time' => $now,
        'create_by' => 'CODEX',
        'personnel_umber' => $marker,
        'permanent_address' => 'CODEX TEST',
        'coach_name' => 'CODEX TEST',
        'sport_year' => '0',
        'is_show' => 1,
    ]);
    if ($adminFunded) {
        $store->name('staff')->where('id', $staffId)->update([
            'ai_user_id' => $aiId, 'job_status' => 1,
        ]);
    }
    $storeUserId = (int)$store->name('user')->insertGetId([
        'uuid' => $userUuid,
        'staff_uuid' => $staffUuid,
        'ai_user_id' => $aiId,
        'account' => $marker,
        'pwd' => 'DISABLED',
        'salt' => strtoupper(bin2hex(random_bytes(8))),
        'name' => $marker,
        'create_time' => $now,
        'create_by' => 'CODEX',
        'is_delete' => 0,
    ]);
    $authId = (int)$store->name('api_auth')->insertGetId([
        'account' => $marker,
        'pwd' => $authPwd,
        'key' => $authKey,
        'type' => 1,
        'status' => 1,
        'start_time' => date('Y-m-d H:i:s', time() - 3600),
        'end_time' => date('Y-m-d H:i:s', time() + 3600),
    ]);
    if (min($aiId, $staffId, $storeUserId, $authId) <= 0) {
        throw new RuntimeException('Synthetic payment identity not inserted');
    }
    if ((int)$store->name('staff')->where('uuid', $staffUuid)->count() !== 1
        || (int)$store->name('user')->where('uuid', $userUuid)->count() !== 1
        || (int)$store->name('api_auth')->where('account', $marker)->count() !== 1
        || (int)$ai->name('user')->where('user_id', $aiId)->count() !== 1) {
        throw new RuntimeException('Synthetic payment identity readback mismatch');
    }
    if (getenv('POC_PAYMENT_HTTP') === '1') {
        $baseUrl = rtrim((string)getenv('POC_BASE_URL'), '/');
        $expectFault = getenv('POC_EXPECT_AI_FAIL') === '1';
        $expectStoreFault = getenv('POC_EXPECT_STORE_FAIL') === '1';
        $expectCommitFault = getenv('POC_EXPECT_COMMIT_FAIL') === '1';
        if ($baseUrl === '' || getenv('POC_EXTERNAL_STUBS') !== '1') {
            throw new RuntimeException('Isolated payment endpoint or external stubs not confirmed');
        }
        $boundary = json_decode((string)file_get_contents($baseUrl . '/__poc_boundary'), true);
        if (empty($boundary['external_stubs']) || empty($boundary['redis_isolated'])
            || ($expectFault && empty($boundary['account_fault']))
            || ($expectStoreFault && empty($boundary['store_order_fault']))
            || ($expectCommitFault && empty($boundary['ai_commit_fault']))
            || (int)$expectFault + (int)$expectStoreFault + (int)$expectCommitFault > 1) {
            throw new RuntimeException('Payment test boundary unavailable');
        }
        $mealModel = (int)$store->name('config')->where('config_key', 'meal_model')->where('status', 1)->value('config_value');
        if ($mealModel === 5) throw new RuntimeException('Zhangjiagang payment branch is outside isolated stub boundary');
        $pdoDsn = $dialect === 'dm'
            ? 'dm:host=' . getenv('POC_HOST') . ';port=' . getenv('POC_PORT') . ';schema=' . getenv('POC_SCHEMA')
            : 'mysql:host=' . getenv('POC_HOST') . ';port=' . getenv('POC_PORT')
                . ';dbname=' . getenv('POC_SCHEMA') . ';charset=utf8mb4';
        $pdo = new PDO($pdoDsn, getenv('POC_USER'), getenv('POC_PASSWORD'),
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
        $restaurants = $dialect === 'dm' ? 'YDY_RESTAURANTS' : 'ydy_restaurants';
        $recipes = $dialect === 'dm' ? 'YDY_RECIPES' : 'ydy_recipes';
        $dishes = $dialect === 'dm' ? 'YDY_DISHES' : 'ydy_dishes';
        $sql = 'SELECT r.ID AS RESTAURANT_ID,x.MENU_DATE,x.MEAL_TIMES,x.DISHES_UUID,d.PRICE'
            . ' FROM ' . $restaurants . ' r'
            . ' JOIN ' . $recipes . ' x ON x.RESTAURANT_ID=r.ID'
            . ' JOIN ' . $dishes . ' d ON d.UUID=x.DISHES_UUID'
            . ' WHERE r.STALL_TYPE=2 AND r.STATUS=1 AND r.IS_DELETE=2'
            . ' AND x.MENU_DATE>=? AND d.STATUS=1 AND d.IS_DEL=1'
            . ' AND d.PRICE>0 AND d.PRICE<=20'
            . ' ORDER BY x.MENU_DATE DESC,x.MEAL_TIMES DESC,x.ID '
            . ($dialect === 'dm' ? 'FETCH FIRST 30 ROWS ONLY' : 'LIMIT 30');
        $stmt = $pdo->prepare($sql);
        $stmt->execute([date('Y-m-d')]);
        $candidates = $stmt->fetchAll(PDO::FETCH_ASSOC);
        $selected = null;
        $confirmedCount = 0;
        $rejections = ['expired' => 0, 'dish' => 0, 'other' => 0];
        $firstRejection = '';
        foreach ($candidates as $candidate) {
            $candidate = array_change_key_case($candidate, CASE_LOWER);
            $request = [
                'restaurant_id' => (int)$candidate['restaurant_id'],
                'meal_date' => substr((string)$candidate['menu_date'], 0, 10),
                'meal_times' => (int)$candidate['meal_times'],
                'dishes' => [['uuid' => (string)$candidate['dishes_uuid'], 'number' => 1]],
            ];
            $context = stream_context_create(['http' => [
                'method' => 'POST',
                'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
                'content' => http_build_query($request, '', '&', PHP_QUERY_RFC3986),
                'timeout' => 15, 'ignore_errors' => true,
            ]]);
            $body = file_get_contents($baseUrl . '/api/orderMachine/confirm', false, $context);
            $confirmed = is_string($body) ? json_decode($body, true) : null;
            if (!is_array($confirmed) || (int)($confirmed['code'] ?? -1) !== 0) {
                $message = (string)($confirmed['message'] ?? '');
                if ($firstRejection === '') $firstRejection = substr($message, 0, 160);
                $category = strpos($message, '已过期') !== false ? 'expired'
                    : (strpos($message, '菜品') !== false ? 'dish' : 'other');
                $rejections[$category]++;
                continue;
            }
            $confirmedCount++;
            $methods = (array)($confirmed['data']['take_methods'] ?? []);
            $available = array_map(static function (array $method): int {
                return (int)($method['type'] ?? 0);
            }, $methods);
            $takeMethod = in_array(1, $available, true) ? 1 : (in_array(2, $available, true) ? 2 : 0);
            if ($takeMethod === 0) continue;
            $request['take_method'] = $takeMethod;
            $selected = $request;
            break;
        }
        if (!$selected) throw new RuntimeException('No low-price fixture passed formal confirm interface; candidates='
            . count($candidates) . ', confirmed=' . $confirmedCount . ', rejections=' . json_encode($rejections)
            . ', first=' . $firstRejection);

        $store->commit();
        $storeStarted = false;
        $authCommitted = true;
        $ai->commit();
        $aiStarted = false;
        echo json_encode(['dialect' => $dialect, 'status' => 'starting_http',
            'marker' => $marker, 'ai_user_id' => $aiId]) . PHP_EOL;

        if ($adminRecharge) {
            require_once $root . '/application/base/Security.php';
            $adminToken = \app\base\Security::encrypt(implode("\t", [
                '1', 'CDX-ADMIN', 'CODEX', '0', '', '', 'CODEX',
            ]));
            $rechargeContext = stream_context_create(['http' => [
                'method' => 'POST',
                'header' => "Content-Type: application/x-www-form-urlencoded\r\n"
                    . 'token: ' . $adminToken . "\r\n",
                'content' => http_build_query([
                    'department_uuid' => '', 'staff_uuids' => $staffUuid,
                    'money' => '100.00', 'pay_method' => 'cash',
                    'note' => $marker,
                ], '', '&', PHP_QUERY_RFC3986),
                'timeout' => 30, 'ignore_errors' => true,
            ]]);
            $rechargeBody = file_get_contents($baseUrl . '/p/rechargeOrder/add',
                false, $rechargeContext);
            $recharge = is_string($rechargeBody)
                ? json_decode($rechargeBody, true) : null;
            $cashBalance = (string)$ai->name('user')->where('user_id', $aiId)
                ->value('balance');
            $rechargeCount = (int)$ai->name('recharge_order')
                ->where('user_id', $aiId)->count();
            $cashLogCount = (int)$ai->name('user_balance_log')
                ->where('user_id', $aiId)->count();
            if (!is_array($recharge) || (int)($recharge['code'] ?? -1) !== 0
                || bccomp($cashBalance, '100.00', 2) !== 0
                || $rechargeCount !== 1 || $cashLogCount !== 1) {
                throw new RuntimeException('Formal admin cash recharge/readback failed: '
                    . json_encode(['code' => $recharge['code'] ?? null,
                        'message' => $recharge['message'] ?? null,
                        'cash_balance' => $cashBalance,
                        'recharge_rows' => $rechargeCount,
                        'cash_log_rows' => $cashLogCount], JSON_UNESCAPED_UNICODE));
            }
            $fundedWallet = $ai->name('user')->where('user_id', $aiId)
                ->field('balance,subsidy_balance,gift_balance')->find();
            echo json_encode(['dialect' => $dialect,
                'status' => 'passed_admin_cash_recharge',
                'marker' => $marker, 'ai_user_id' => $aiId,
                'cash_balance' => $cashBalance,
                'recharge_rows' => $rechargeCount,
                'cash_log_rows' => $cashLogCount]) . PHP_EOL;
        }
        if ($adminSubsidy) {
            require_once $root . '/application/base/Security.php';
            $adminToken = \app\base\Security::encrypt(implode("\t", [
                '1', 'CDX-ADMIN', 'CODEX', '0', '', '', 'CODEX',
            ]));
            $adminPost = static function (string $route, array $form)
                use ($baseUrl, $adminToken): array {
                $context = stream_context_create(['http' => [
                    'method' => 'POST',
                    'header' => "Content-Type: application/x-www-form-urlencoded\r\n"
                        . 'token: ' . $adminToken . "\r\n",
                    'content' => http_build_query($form, '', '&', PHP_QUERY_RFC3986),
                    'timeout' => 30, 'ignore_errors' => true,
                ]]);
                $body = file_get_contents($baseUrl . $route, false, $context);
                $response = is_string($body) ? json_decode($body, true) : null;
                if (!is_array($response)) {
                    throw new RuntimeException('Admin subsidy HTTP response not JSON');
                }
                return $response;
            };
            $submitted = $adminPost('/api/subsidy/submit', [
                'staff_uuids' => $staffUuid, 'money' => '20.00',
                'name' => $marker, 'note' => $marker,
                'department_uuids' => '',
                'expiration_start_time' => date('Y-m-d H:i:s', time() - 3600),
                'expiration_end_time' => '2099-12-31 23:59:59',
            ]);
            $subsidyLogId = (int)($submitted['data']['log_id'] ?? 0);
            if ((int)($submitted['code'] ?? -1) !== 0 || $subsidyLogId <= 0) {
                throw new RuntimeException('Formal subsidy submission failed: '
                    . (string)($submitted['message'] ?? 'non-JSON'));
            }
            $review = $adminPost('/api/subsidy/status', [
                'log_id' => $subsidyLogId, 'status' => 3,
            ]);
            $final = $adminPost('/api/subsidy/status', [
                'log_id' => $subsidyLogId, 'status' => 5,
            ]);
            $grantStatus = (int)$store->name('subsidy_log')
                ->where('id', $subsidyLogId)->value('status');
            $subsidyRows = $ai->name('user_subsidy')->where('user_id', $aiId)
                ->where('log_id', $subsidyLogId)->select();
            $subsidyAmount = $ai->name('user_subsidy')->where('user_id', $aiId)
                ->where('log_id', $subsidyLogId)->sum('money');
            $subsidyLogRows = (int)$ai->name('user_subsidy_log')
                ->where('user_id', $aiId)->count();
            if ((int)($review['code'] ?? -1) !== 0
                || (int)($final['code'] ?? -1) !== 0 || $grantStatus !== 5
                || count((array)$subsidyRows) !== 1
                || bccomp((string)$subsidyAmount, '20.00', 2) !== 0
                || $subsidyLogRows !== 1) {
                throw new RuntimeException('Formal subsidy grant/readback failed: '
                    . json_encode(['review_code' => $review['code'] ?? null,
                        'review_message' => $review['message'] ?? null,
                        'final_code' => $final['code'] ?? null,
                        'final_message' => $final['message'] ?? null,
                        'grant_status' => $grantStatus,
                        'subsidy_rows' => count((array)$subsidyRows),
                        'subsidy_amount' => (string)$subsidyAmount,
                        'log_rows' => $subsidyLogRows], JSON_UNESCAPED_UNICODE));
            }
            $fundedWallet = $ai->name('user')->where('user_id', $aiId)
                ->field('balance,subsidy_balance,gift_balance')->find();
            $fundedWallet['local_subsidy'] = (string)$subsidyAmount;
            echo json_encode(['dialect' => $dialect,
                'status' => 'passed_admin_subsidy_grant',
                'marker' => $marker, 'ai_user_id' => $aiId,
                'subsidy_log_id' => $subsidyLogId,
                'grant_amount' => (string)$subsidyAmount,
                'wallet_log_rows' => $subsidyLogRows]) . PHP_EOL;
        }

        $payment = $selected + [
            'staff_uuid' => $staffUuid,
            'msgid' => $marker,
            'order_type' => 1,
            'equipment_code' => '',
        ];
        $paymentBody = http_build_query($payment, '', '&', PHP_QUERY_RFC3986);
        $forcedOrderNo = date('ymdHi') . sprintf('%05d', random_int(1, 99999));
        $paymentContext = stream_context_create(['http' => [
            'method' => 'POST',
            'header' => "Content-Type: application/x-www-form-urlencoded\r\n"
                . 'account: ' . $marker . "\r\n"
                . 'pwd: ' . $authPwd . "\r\n"
                . 'key: ' . $authKey . "\r\n"
                . 'X-Poc-Order-No: ' . $forcedOrderNo . "\r\n",
            'content' => $paymentBody,
            'timeout' => 30, 'ignore_errors' => true,
        ]]);
        $firstBody = file_get_contents($baseUrl . '/api/orderMachine/pay', false, $paymentContext);
        $first = is_string($firstBody) ? json_decode($firstBody, true) : null;
        if ($expectCommitFault) {
            $redis = new \Redis();
            $redis->connect((string)getenv('POC_REDIS_HOST'), (int)getenv('POC_REDIS_PORT'), 1.0);
            $faultReached = $redis->get('poc:ai_commit_fault') === '1';
            $redis->del('poc:ai_commit_fault');
            $partialOrder = $store->name('meal_order')->where('msgid', $marker)->find();
            $partialNo = (string)($partialOrder['order_no'] ?? '');
            $aiSettlementCount = $partialNo !== ''
                ? (int)$ai->name('account_settlement')->where('biz_order_no', $partialNo)->count() : 0;
            $aiBalance = (float)$ai->name('user')->where('user_id', $aiId)->value('balance');
            if (!$faultReached || !$partialOrder || $partialNo !== $forcedOrderNo
                || $aiSettlementCount !== 0 || abs($aiBalance - 100.0) > 0.01
                || (int)($first['code'] ?? -1) !== 2) {
                throw new RuntimeException('Injected AI commit fault did not leave the expected pending state');
            }
            $retryUrl = rtrim((string)getenv('POC_RETRY_URL'), '/');
            $retryBoundary = $retryUrl !== ''
                ? json_decode((string)file_get_contents($retryUrl . '/__poc_boundary'), true) : null;
            if (!$retryUrl || empty($retryBoundary['external_stubs'])
                || empty($retryBoundary['redis_isolated'])
                || !empty($retryBoundary['ai_commit_fault'])) {
                throw new RuntimeException('Normal retry endpoint boundary unavailable');
            }
            $retryBody = file_get_contents($retryUrl . '/api/orderMachine/pay', false, $paymentContext);
            $retry = is_string($retryBody) ? json_decode($retryBody, true) : null;
            if ((int)($retry['code'] ?? -1) !== 2
                || (int)$store->name('meal_order')->where('msgid', $marker)->count() !== 1
                || (int)$ai->name('account_settlement')->where('biz_order_no', $partialNo)->count() !== 0
                || abs((float)$ai->name('user')->where('user_id', $aiId)->value('balance') - 100.0) > 0.01) {
                throw new RuntimeException('Duplicate request reported success after partial AI commit');
            }
            echo json_encode(['dialect' => $dialect, 'status' => 'passed_ai_commit_fault_pending',
                'marker' => $marker, 'fault_reached' => true, 'store_order_id' => (int)$partialOrder['id'],
                'store_orders' => 1, 'ai_settlements' => 0, 'ai_balance_unchanged' => true,
                'duplicate_retry_pending' => true]) . PHP_EOL;
            return;
        }
        if ($expectFault || $expectStoreFault) {
            $redis = new \Redis();
            $redis->connect((string)getenv('POC_REDIS_HOST'), (int)getenv('POC_REDIS_PORT'), 1.0);
            $faultKey = ($expectStoreFault ? 'poc:store_order_fault:' : 'poc:account_fault:') . $forcedOrderNo;
            $faultReached = $redis->get($faultKey) === '1';
            $redis->del($faultKey);
            $storeOrderCount = (int)$store->name('meal_order')->where('msgid', $marker)->count();
            $aiSettlementCount = (int)$ai->name('account_settlement')->where('biz_order_no', $forcedOrderNo)->count();
            $aiBalance = (float)$ai->name('user')->where('user_id', $aiId)->value('balance');
            if (!is_array($first) || (int)($first['code'] ?? 0) === 0 || !$faultReached
                || $storeOrderCount !== 0 || $aiSettlementCount !== 0 || abs($aiBalance - 100.0) > 0.01) {
                throw new RuntimeException('Injected store/account failure was not rolled back');
            }
            echo json_encode(['dialect' => $dialect,
                'status' => $expectStoreFault ? 'passed_store_fault_rollback' : 'passed_ai_fault_rollback',
                'marker' => $marker, 'fault_reached' => true, 'store_orders' => 0,
                'ai_settlements' => 0, 'ai_balance_unchanged' => true]) . PHP_EOL;
            return;
        }
        if (!is_array($first) || (int)($first['code'] ?? -1) !== 0) {
            throw new RuntimeException('Formal payment failed: ' . substr((string)($first['message'] ?? 'non-JSON'), 0, 120));
        }
        $order = $store->name('meal_order')->where('msgid', $marker)->find();
        if (!$order || (int)$order['user_id'] !== $storeUserId
            || (string)$order['order_no'] !== $forcedOrderNo || (float)$order['pay_price'] <= 0) {
            throw new RuntimeException('Store order missing or mismatched after payment');
        }
        $orderId = (int)$order['id'];
        $orderNo = (string)$order['order_no'];
        $settlement = $ai->name('account_settlement')->where('biz_order_no', $orderNo)
            ->where('user_id', $aiId)->where('biz_type', 'consume')->find();
        $settlements = $settlement ? 1 : 0;
        $cashLogs = (int)$ai->name('user_balance_log')->where('order_id', $orderId)
            ->where('user_id', $aiId)->count();
        $giftLogs = $adminFunded ? (int)$ai->name('user_gift_log')
            ->where('biz_order_id', $orderId)->where('user_id', $aiId)
            ->where('biz_type', 'consume')->count() : 0;
        $subsidyLogs = $adminFunded ? (int)$ai->name('user_subsidy_log')
            ->where('order_id', $orderId)->where('user_id', $aiId)->count() : 0;
        $menuRows = (int)$store->name('initial_menu')->where('meal_order_id', $orderId)->count();
        $pickupRows = (int)$store->name('meal_order_pickup')->where('meal_order_id', $orderId)->count();
        $splitValid = true;
        if ($adminFunded && $settlement) {
            $splitCash = (string)$settlement['cash_amount'];
            $splitSubsidy = (string)$settlement['subsidy_amount'];
            $splitGift = (string)$settlement['gift_amount'];
            $paidWallet = $ai->name('user')->where('user_id', $aiId)
                ->field('balance,subsidy_balance,gift_balance')->find();
            $paidSubsidy = (string)$ai->name('user_subsidy')
                ->where('user_id', $aiId)->sum('money');
            $splitValid = bccomp(bcadd(bcadd($splitCash, $splitSubsidy, 2), $splitGift, 2),
                    (string)$order['pay_price'], 2) === 0
                && bccomp(bcsub((string)$fundedWallet['balance'], (string)$paidWallet['balance'], 2),
                    $splitCash, 2) === 0
                && bccomp(bcsub((string)($fundedWallet['local_subsidy'] ?? '0'),
                    $paidSubsidy, 2), $splitSubsidy, 2) === 0
                && bccomp(bcsub((string)$fundedWallet['gift_balance'],
                    (string)$paidWallet['gift_balance'], 2), $splitGift, 2) === 0
                && $cashLogs === (bccomp($splitCash, '0.00', 2) > 0 ? 1 : 0)
                && $subsidyLogs === (bccomp($splitSubsidy, '0.00', 2) > 0 ? 1 : 0)
                && $giftLogs === (bccomp($splitGift, '0.00', 2) > 0 ? 1 : 0);
        }
        if ($settlements !== 1 || (!$adminFunded && $cashLogs !== 1)
            || !$splitValid || $menuRows < 1 || $pickupRows !== 1) {
            throw new RuntimeException('Cross-database order/account/pickup persistence mismatch');
        }
        $repeatBody = file_get_contents($baseUrl . '/api/orderMachine/pay', false, $paymentContext);
        $repeat = is_string($repeatBody) ? json_decode($repeatBody, true) : null;
        if (!is_array($repeat) || (int)($repeat['code'] ?? -1) !== 0
            || (int)$store->name('meal_order')->where('msgid', $marker)->count() !== 1
            || (int)$ai->name('account_settlement')->where('biz_order_no', $orderNo)->count() !== 1
            || (!$adminFunded && (int)$ai->name('user_balance_log')
                ->where('order_id', $orderId)->count() !== 1)) {
            throw new RuntimeException('Duplicate formal payment changed persisted order or ledger');
        }
        if ($adminFunded) {
            $repeatWallet = $ai->name('user')->where('user_id', $aiId)
                ->field('balance,subsidy_balance,gift_balance')->find();
            $repeatSubsidy = (string)$ai->name('user_subsidy')
                ->where('user_id', $aiId)->sum('money');
            $repeatCashLogs = (int)$ai->name('user_balance_log')
                ->where('order_id', $orderId)->where('user_id', $aiId)->count();
            $repeatGiftLogs = (int)$ai->name('user_gift_log')
                ->where('biz_order_id', $orderId)->where('user_id', $aiId)
                ->where('biz_type', 'consume')->count();
            $repeatSubsidyLogs = (int)$ai->name('user_subsidy_log')
                ->where('order_id', $orderId)->where('user_id', $aiId)->count();
            if (bccomp((string)$repeatWallet['balance'],
                    (string)$paidWallet['balance'], 2) !== 0
                || bccomp((string)$repeatWallet['gift_balance'],
                    (string)$paidWallet['gift_balance'], 2) !== 0
                || bccomp($repeatSubsidy, $paidSubsidy, 2) !== 0
                || $repeatCashLogs !== $cashLogs
                || $repeatGiftLogs !== $giftLogs
                || $repeatSubsidyLogs !== $subsidyLogs) {
                throw new RuntimeException('Duplicate formal payment changed wallet or component logs');
            }
        }
        echo json_encode(['dialect' => $dialect, 'status' => 'passed', 'marker' => $marker,
            'store_order_id' => $orderId, 'ai_user_id' => $aiId, 'order_no' => $orderNo,
            'settlements' => $settlements, 'cash_logs' => $cashLogs, 'menu_rows' => $menuRows,
            'pickup_rows' => $pickupRows, 'gift_logs' => $giftLogs,
            'subsidy_logs' => $subsidyLogs,
            'payment_split' => $adminFunded ? [
                'cash' => $splitCash, 'subsidy' => $splitSubsidy, 'gift' => $splitGift,
            ] : null,
            'duplicate_idempotent' => true]) . PHP_EOL;
    } else {
    $store->rollback();
    $storeStarted = false;
    $ai->rollback();
    $aiStarted = false;
    if ((int)$store->name('staff')->where('uuid', $staffUuid)->count() !== 0
        || (int)$store->name('user')->where('uuid', $userUuid)->count() !== 0
        || (int)$store->name('api_auth')->where('account', $marker)->count() !== 0
        || (int)$ai->name('user')->where('user_id', $aiId)->count() !== 0) {
        throw new RuntimeException('Synthetic payment identity rollback left rows');
    }
    echo json_encode(['dialect' => $dialect, 'status' => 'passed',
        'store_staff_user_auth' => true, 'ai_account' => true, 'rollback_clean' => true]) . PHP_EOL;
    }
} finally {
    if ($storeStarted) $store->rollback();
    if ($aiStarted) $ai->rollback();
    if ($authCommitted) {
        $store->name('api_auth')->where('id', $authId)->where('account', $marker)->delete();
        if ($expectCommitFault) {
            $partialOrder = $store->name('meal_order')->where('msgid', $marker)->find();
            if ($partialOrder) {
                $partialId = (int)$partialOrder['id'];
                $store->startTrans();
                try {
                    $store->name('meal_order_pickup')->where('meal_order_id', $partialId)->delete();
                    $store->name('initial_menu')->where('meal_order_id', $partialId)->delete();
                    $store->name('meal_order')->where('id', $partialId)->where('msgid', $marker)->delete();
                    $store->commit();
                } catch (\Throwable $cleanupError) {
                    $store->rollback();
                    throw $cleanupError;
                }
            }
            $store->name('user')->where('id', $storeUserId)->where('uuid', $userUuid)->delete();
            $store->name('staff')->where('id', $staffId)->where('uuid', $staffUuid)->delete();
            $ai->name('user')->where('user_id', $aiId)->where('nick_name', $marker)->delete();
            if ((int)$store->name('meal_order')->where('msgid', $marker)->count() !== 0
                || (int)$ai->name('user')->where('user_id', $aiId)->count() !== 0) {
                throw new RuntimeException('Synthetic commit fault cleanup left data');
            }
        }
    }
}
