<?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, 'ONECARD_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_account_settlement', 'yoshop_user_balance_log', 'yoshop_user_subsidy_log'] as $table) {
    if (!in_array($table, $tables, true)) throw new RuntimeException('Required account table missing: ' . $table);
}

$storeId = 900000000 + random_int(10000, 99999);
$orderId = random_int(800000000, 899999999);
$orderNo = 'OCS' . strtoupper(bin2hex(random_bytes(6)));
$refundNo = 'OCR' . strtoupper(bin2hex(random_bytes(6)));
if ((int)$db->name('user')->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, 'refund_no' => $refundNo]) . PHP_EOL;

$outerStarted = false;
try {
    $db->startTrans();
    $outerStarted = true;
    $userId = (int)$db->name('user')->insertGetId([
        'store_id' => $storeId,
        'mobile' => '',
        'nick_name' => 'CODEXDM8_ONECARD',
        'balance' => '100.00',
        'subsidy_balance' => '5.00',
        'gift_balance' => '7.00',
        'is_delete' => 0,
        'create_time' => time(),
    ]);
    if ($userId <= 0) throw new RuntimeException('Synthetic user insert failed');
    $service = new \app\common\service\account\AccountSettlementService();
    $consumeParams = [
        'user_id' => $userId,
        'order_id' => $orderId,
        'order_no' => $orderNo,
        'amount' => '5.00',
        'cash_amount' => '3.00',
        'subsidy_amount' => '2.00',
        'cash_balance_after' => '97.00',
        'subsidy_balance_after' => '3.00',
        'one_card_trade_id' => 700001,
        'store_id' => $storeId,
    ];
    $consume = $service->recordOneCardConsume($db, $consumeParams);
    $repeatConsume = $service->recordOneCardConsume($db, $consumeParams);
    $afterConsume = $db->name('user')->where('user_id', $userId)->field('balance,subsidy_balance,gift_balance')->find();
    if ((int)$consume['settlement_id'] !== (int)$repeatConsume['settlement_id']
        || $consume['cash'] !== '3.00' || $consume['subsidy'] !== '2.00'
        || number_format((float)$afterConsume['balance'], 2, '.', '') !== '97.00'
        || number_format((float)$afterConsume['subsidy_balance'], 2, '.', '') !== '3.00'
        || number_format((float)$afterConsume['gift_balance'], 2, '.', '') !== '7.00'
        || (int)$db->name('user_balance_log')->where(['user_id' => $userId, 'order_id' => $orderId, 'scene' => 20])->count() !== 1
        || (int)$db->name('user_subsidy_log')->where(['user_id' => $userId, 'order_id' => $orderId, 'scene' => 20])->count() !== 1) {
        throw new RuntimeException('One-card consume balance/ledger/idempotency mismatch');
    }

    $refundParams = [
        'user_id' => $userId,
        'order_id' => $orderId,
        'order_no' => $orderNo,
        'refund_no' => $refundNo,
        'amount' => '2.00',
        'cash_amount' => '1.00',
        'subsidy_amount' => '1.00',
        'cash_balance_after' => '98.00',
        'subsidy_balance_after' => '4.00',
        'one_card_trade_id' => 700002,
        'store_id' => $storeId,
    ];
    $refund = $service->recordOneCardRefund($db, $refundParams);
    $repeatRefund = $service->recordOneCardRefund($db, $refundParams);
    $afterRefund = $db->name('user')->where('user_id', $userId)->field('balance,subsidy_balance,gift_balance')->find();
    if ((int)$refund['settlement_id'] !== (int)$repeatRefund['settlement_id']
        || $refund['cash'] !== '1.00' || $refund['subsidy'] !== '1.00'
        || number_format((float)$afterRefund['balance'], 2, '.', '') !== '98.00'
        || number_format((float)$afterRefund['subsidy_balance'], 2, '.', '') !== '4.00'
        || number_format((float)$afterRefund['gift_balance'], 2, '.', '') !== '7.00'
        || (int)$db->name('account_settlement')->where(['store_id' => $storeId, 'biz_order_no' => $orderNo, 'refund_no' => $refundNo, 'status' => 20])->count() !== 1
        || (int)$db->name('user_balance_log')->where(['user_id' => $userId, 'order_id' => $orderId, 'scene' => 40])->count() !== 1) {
        throw new RuntimeException('One-card refund balance/ledger/idempotency mismatch');
    }

    $db->rollback();
    $outerStarted = false;
    $dsn = $dialect === 'dm' ? "dm:host=$host;port=$port;schema=$schema" : "mysql:host=$host;port=$port;dbname=$schema;charset=utf8mb4";
    $fresh = new PDO($dsn, $user, $password, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
    foreach (['user', 'account_settlement', 'user_balance_log', 'user_subsidy_log'] as $table) {
        $name = ($dialect === 'dm' ? 'YOSHOP_' . strtoupper($table) : 'yoshop_' . $table);
        $stmt = $fresh->prepare('SELECT COUNT(*) FROM ' . $name . ' WHERE STORE_ID=?');
        $stmt->execute([$storeId]);
        if ((int)$stmt->fetchColumn() !== 0) throw new RuntimeException('Rollback left synthetic rows in ' . $table);
    }
    echo json_encode(['dialect' => $dialect, 'status' => 'passed', 'store_id' => $storeId,
        'consume_settlement_id' => (int)$consume['settlement_id'], 'refund_settlement_id' => (int)$refund['settlement_id'],
        'consume_idempotent' => true, 'refund_idempotent' => true, 'cash_subsidy_balances' => 'matched', 'rollback_clean' => true]) . PHP_EOL;
} finally {
    if ($outerStarted) $db->rollback();
}
