<?php

declare(strict_types=1);

$root = getenv('POC_APP_ROOT');
$dialect = getenv('POC_DIALECT');
$host = getenv('POC_HOST');
$port = (int)getenv('POC_PORT');
$schema = getenv('POC_SCHEMA');
$user = getenv('POC_USER');
$password = getenv('POC_PASSWORD');
if (!$root || !in_array($dialect, ['mysql', 'dm'], true) || !$host || $port < 1 || !$schema || !$user || !$password) exit(2);
define('APP_PATH', $root . '/application/');
require $root . '/thinkphp/base.php';
require $root . '/application/common.php';
set_exception_handler(static function (\Throwable $e): void {
    fwrite(STDERR, 'GIFT_PROBE_FAIL ' . get_class($e) . ': ' . substr($e->getMessage(), 0, 250) . PHP_EOL);
    exit(1);
});
if (!function_exists('config')) {
    function config($name = '', $value = null, $range = '')
    {
        return $value === null ? \think\Config::get($name) : \think\Config::set($name, $value, $range);
    }
}

$config = [
    'type' => $dialect === 'dm' ? \app\cores\database\Dm::class : 'mysql',
    'hostname' => $host,
    'hostport' => $port,
    'database' => $schema,
    'username' => $user,
    'password' => $password,
    'prefix' => 'yoshop_',
    'debug' => false,
    'resultset_type' => 'array',
    'fields_strict' => true,
    'auto_timestamp' => false,
];
$config['ai_zhct'] = $config;
\think\Config::set($config, 'database');
\think\Db::clear();
$db = get_ai_db();
$tables = array_map('strtolower', $db->getTables());
foreach (['yoshop_user', 'yoshop_recharge_gift_rule', 'yoshop_recharge_order', 'yoshop_user_balance_log', 'yoshop_user_gift_log'] as $table) {
    if (!in_array($table, $tables, true)) throw new RuntimeException('Required account table missing: ' . $table);
}

$storeId = 900000000 + random_int(10000, 99999);
$orderNo = 'CDX' . strtoupper(bin2hex(random_bytes(6)));
if ((int)$db->name('user')->where('store_id', $storeId)->count() !== 0
    || (int)$db->name('recharge_gift_rule')->where('store_id', $storeId)->count() !== 0) {
    throw new RuntimeException('Synthetic store ID already exists');
}
echo json_encode(['dialect' => $dialect, 'status' => 'starting', 'store_id' => $storeId, 'order_no' => $orderNo]) . PHP_EOL;

$outerStarted = false;
try {
    $db->startTrans();
    $outerStarted = true;
    $userId = (int)$db->name('user')->insertGetId([
        'store_id' => $storeId,
        'mobile' => '',
        'nick_name' => 'CODEXDM8_COMPAT',
        'balance' => '0.00',
        'gift_balance' => '0.00',
        'is_delete' => 0,
        'create_time' => time(),
    ]);
    $otherUserId = (int)$db->name('user')->insertGetId([
        'store_id' => $storeId,
        'mobile' => '',
        'nick_name' => 'CODEXDM8_OTHER',
        'balance' => '0.00',
        'gift_balance' => '0.00',
        'is_delete' => 0,
        'create_time' => time(),
    ]);
    $ruleId = (int)$db->name('recharge_gift_rule')->insertGetId([
        'store_id' => $storeId,
        'rule_name' => 'CODEXDM8_TEST_RULE',
        'pay_methods' => '["cash"]',
        'min_money' => '1.00',
        'gift_money' => '0.50',
        'status' => 1,
        'is_delete' => 0,
    ]);
    if ($userId <= 0 || $otherUserId <= 0 || $ruleId <= 0) throw new RuntimeException('Synthetic account fixture insert failed');

    $params = [
        'user_id' => $userId,
        'order_no' => $orderNo,
        'recharge_type' => 10,
        'platform' => 'CODEX',
        'store_id' => $storeId,
        'note' => 'CODEXDM8 compatibility rollback test',
        'create_by' => 'Codex',
        'pay_price' => '1.00',
        'pay_method' => 'cash',
        'trade_id' => 0,
        'balance_scene' => 30,
        'operator_name' => 'Codex',
    ];
    $service = new \app\common\service\account\GiftRechargeService();
    $first = $service->createPaidRecharge($db, $params);
    $repeat = $service->createPaidRecharge($db, $params);
    if ((int)$first['order_id'] !== (int)$repeat['order_id'] || $first['gift_money'] !== '0.50' || $first['actual_money'] !== '1.50') {
        throw new RuntimeException('Gift recharge allocation or idempotency failed');
    }
    $account = $db->name('user')->where('user_id', $userId)->field('balance,gift_balance')->find();
    $cashLogs = (int)$db->name('user_balance_log')->where('order_id', (int)$first['order_id'])->where('user_id', $userId)->count();
    $giftLogs = (int)$db->name('user_gift_log')->where('biz_order_no', $orderNo)->where('user_id', $userId)->count();
    if (number_format((float)$account['balance'], 2, '.', '') !== '1.00'
        || number_format((float)$account['gift_balance'], 2, '.', '') !== '0.50'
        || $cashLogs !== 1 || $giftLogs !== 1) {
        throw new RuntimeException('Cash/gift balances or ledger rows differ');
    }

    try {
        $service->createPaidRecharge($db, array_merge($params, ['user_id' => $otherUserId]));
        throw new RuntimeException('Cross-user order replay was accepted');
    } catch (\RuntimeException $expected) {
        if ($expected->getMessage() !== '充值订单号已被其他用户使用') throw $expected;
    }
    $other = $db->name('user')->where('user_id', $otherUserId)->field('balance,gift_balance')->find();
    if (number_format((float)$other['balance'], 2, '.', '') !== '0.00'
        || number_format((float)$other['gift_balance'], 2, '.', '') !== '0.00') {
        throw new RuntimeException('Rejected replay changed the other account');
    }

    $db->rollback();
    $outerStarted = false;
    foreach (['user', 'recharge_gift_rule', 'recharge_order', 'user_balance_log', 'user_gift_log'] as $table) {
        if ((int)$db->name($table)->where('store_id', $storeId)->count() !== 0) {
            throw new RuntimeException('Rollback left synthetic account data in ' . $table);
        }
    }
    echo json_encode(['dialect' => $dialect, 'status' => 'passed', 'store_id' => $storeId,
        'gift_rule' => $ruleId, 'order_id' => (int)$first['order_id'], 'cash' => '1.00', 'gift' => '0.50',
        'idempotent' => true, 'cross_user_replay_rejected' => true, 'rollback_clean' => true]) . PHP_EOL;
} finally {
    if ($outerStarted) $db->rollback();
}
