<?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';
require_once $root . '/application/api/service/Consume.php';
if (!function_exists('config')) {
    function config($name = '', $value = null, $range = '')
    {
        return $value === null ? \think\Config::get($name) : \think\Config::set($name, $value, $range);
    }
}
\think\Config::set([
    'type' => $dialect === 'dm' ? \app\cores\database\Dm::class : 'mysql',
    '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'), 'prefix' => 'yoshop_',
    ],
], 'database');
\think\Db::clear();
$db = \think\Db::connect();
$marker = 'CDXGUARD' . strtoupper(bin2hex(random_bytes(5)));
$staff = 'CDXGUARD-' . strtoupper(bin2hex(random_bytes(12)));
$orderNo = 'CDXGUARD-' . strtoupper(bin2hex(random_bytes(8)));
$id = 0;
$db->startTrans();
try {
    $id = (int)$db->name('one_card_trade_order')->insertGetId([
        'business' => $marker, 'local_repo' => 'store', 'local_scene' => 'consume',
        'local_order_type' => 'meal_order', 'local_order_no' => $orderNo,
        'msgid' => $marker, 'staff_uuid' => $staff, 'trade_type' => 'consume',
        'status' => 'unknown',
    ]);
    if ($id <= 0) throw new RuntimeException('Test trade insert failed');
    $method = new ReflectionMethod(\app\api\service\Consume::class, 'findPriorOneCardConsumeTrade');
    $method->setAccessible(true);
    $service = new \app\api\service\Consume();
    $same = $method->invoke($service, $marker);
    $otherMsgid = $method->invoke($service, 'OTHER-MSGID');
    if ((int)($same['id'] ?? 0) !== $id
        || (string)($same['local_order_no'] ?? '') !== $orderNo
        || (string)($same['staff_uuid'] ?? '') !== $staff
        || $otherMsgid !== []) {
        throw new RuntimeException('One-card msgid guard query mismatch');
    }
    echo json_encode(['dialect' => $dialect, 'insert_readback' => true,
        'same_msgid_guarded' => true, 'cross_identity_record_detected' => true]) . PHP_EOL;
} finally {
    $db->rollback();
    $after = $db->name('one_card_trade_order')->where('id', $id)->count();
    if ((int)$after !== 0) throw new RuntimeException('Test trade rollback left a row');
}
$sourceMarker = (string)getenv('POC_MARKER');
if ($sourceMarker !== '') {
    if (!preg_match('/^CDXPAY[A-F0-9]{8}$/', $sourceMarker)) exit(2);
    $order = $db->name('meal_order')->where('msgid', $sourceMarker)->find();
    if (!$order || (int)($order['account_settlement_id'] ?? 0) <= 0) {
        throw new RuntimeException('Known test payment settlement was not found');
    }
    $settlementCheck = new ReflectionMethod(\app\api\service\Consume::class, 'isCommittedOrderSettlementConfirmed');
    $settlementCheck->setAccessible(true);
    if (!$settlementCheck->invoke($service, $order)) {
        $byId = (int)get_ai_db()->name('account_settlement')
            ->where('settlement_id', (int)$order['account_settlement_id'])->count();
        $byNo = (int)get_ai_db()->name('account_settlement')
            ->where('biz_order_no', (string)$order['order_no'])->count();
        $actual = get_ai_db()->name('account_settlement')
            ->where('settlement_id', (int)$order['account_settlement_id'])->find();
        $expectedAiUser = (int)$db->name('user')->where('id', (int)$order['user_id'])->value('ai_user_id');
        throw new RuntimeException('Committed settlement was not confirmed: by_id=' . $byId
            . ' by_order_no=' . $byNo . ' fields=' . json_encode([
                'order_store' => (int)($order['store_id'] ?? 0),
                'settlement_store' => (int)($actual['store_id'] ?? 0),
                'expected_ai_user' => $expectedAiUser,
                'settlement_user' => (int)($actual['user_id'] ?? 0),
                'order_pay' => (string)($order['pay_price'] ?? ''),
                'settlement_amount' => (string)($actual['amount'] ?? ''),
                'status' => (int)($actual['status'] ?? -1),
                'is_delete' => (int)($actual['is_delete'] ?? -1),
            ]));
    }
    $missing = $order instanceof \think\Model ? $order->toArray() : (array)$order;
    $missing['account_settlement_id'] = 999999999;
    if ($settlementCheck->invoke($service, $missing)) {
        throw new RuntimeException('Missing account settlement was misreported as committed');
    }
    $originalSettlementId = (int)$order['account_settlement_id'];
    $db->startTrans();
    try {
        $db->name('meal_order')->where('id', (int)$order['id'])
            ->update(['account_settlement_id' => 999999999]);
        $duplicateCheck = new ReflectionMethod(\app\api\service\Consume::class, 'getAsyncOrderResultByMsgid');
        $duplicateCheck->setAccessible(true);
        $result = $duplicateCheck->invoke($service, new Redis(), $sourceMarker,
            (string)$order['meal_date'] . ' 12:00:00', (int)$order['source']);
        if ((int)($result['code'] ?? -1) !== 2) {
            throw new RuntimeException('Duplicate payment accepted an uncommitted AI settlement');
        }
    } finally {
        $db->rollback();
    }
    $restoredSettlementId = (int)$db->name('meal_order')->where('id', (int)$order['id'])
        ->value('account_settlement_id');
    if ($restoredSettlementId !== $originalSettlementId) {
        throw new RuntimeException('Test order settlement link was not restored');
    }
    $aiDb = get_ai_db();
    $aiDb->startTrans();
    try {
        foreach ([10, 30] as $badStatus) {
            $aiDb->name('account_settlement')->where('settlement_id', $originalSettlementId)
                ->update(['status' => $badStatus]);
            if ($settlementCheck->invoke($service, $order)) {
                throw new RuntimeException('Pending/failed AI settlement was accepted');
            }
            $duplicateCheck = new ReflectionMethod(\app\api\service\Consume::class, 'getAsyncOrderResultByMsgid');
            $duplicateCheck->setAccessible(true);
            $result = $duplicateCheck->invoke($service, new Redis(), $sourceMarker,
                (string)$order['meal_date'] . ' 12:00:00', (int)$order['source']);
            if ((int)($result['code'] ?? -1) !== 2) {
                throw new RuntimeException('Duplicate accepted pending/failed AI settlement');
            }
        }
        $aiDb->name('account_settlement')->where('settlement_id', $originalSettlementId)
            ->update(['status' => 20, 'amount' => '999.99']);
        if ($settlementCheck->invoke($service, $order)) {
            throw new RuntimeException('AI settlement amount mismatch was accepted');
        }
    } finally {
        $aiDb->rollback();
    }
    if (!$settlementCheck->invoke($service, $order)) {
        throw new RuntimeException('AI settlement was not restored after rollback');
    }
    echo json_encode(['dialect' => $dialect, 'real_settlement_confirmed' => true,
        'missing_settlement_blocked' => true, 'duplicate_http_gate_pending' => true,
        'pending_failed_and_amount_mismatch_blocked' => true,
        'test_order_restored' => true]) . PHP_EOL;
}
