<?php

declare(strict_types=1);

require __DIR__ . '/../env.php';
require __DIR__ . '/../vendor/autoload.php';

use app\api\model\recharge\Order as RechargeOrderModel;
use app\api\service\Notify as NotifyService;
use app\api\service\recharge\PaySuccess as RechargePaySuccess;
use app\api\service\recharge\WechatRechargeReconcilePolicy;
use app\common\library\payment\gateway\driver\wechat\V3 as WechatV3;

function assertWechatRechargeSame($expected, $actual, string $message): void
{
    if ($expected !== $actual) {
        fwrite(STDERR, $message . PHP_EOL);
        fwrite(STDERR, 'Expected: ' . json_encode($expected, JSON_UNESCAPED_UNICODE) . PHP_EOL);
        fwrite(STDERR, 'Actual:   ' . json_encode($actual, JSON_UNESCAPED_UNICODE) . PHP_EOL);
        exit(1);
    }
}

function assertWechatRechargeContains(string $needle, string $file, string $message): void
{
    $source = file_get_contents(__DIR__ . '/../' . $file);
    if ($source === false || strpos($source, $needle) === false) {
        fwrite(STDERR, $message . ': ' . $needle . PHP_EOL);
        exit(1);
    }
}

assertWechatRechargeSame(
    WechatRechargeReconcilePolicy::ACTION_SUCCESS,
    WechatRechargeReconcilePolicy::classify(['tradeState' => 'SUCCESS', 'paySuccess' => true]),
    '微信成功交易必须进入幂等入账'
);
foreach (['NOTPAY', 'USERPAYING'] as $state) {
    assertWechatRechargeSame(
        WechatRechargeReconcilePolicy::ACTION_WAIT,
        WechatRechargeReconcilePolicy::classify(['tradeState' => $state, 'paySuccess' => false]),
        '微信未支付或支付中交易必须等待重试'
    );
}
foreach (['CLOSED', 'REVOKED', 'PAYERROR'] as $state) {
    assertWechatRechargeSame(
        WechatRechargeReconcilePolicy::ACTION_CLOSED,
        WechatRechargeReconcilePolicy::classify(['tradeState' => $state, 'paySuccess' => false]),
        '微信终态失败交易必须停止自动入账'
    );
}
assertWechatRechargeSame(
    WechatRechargeReconcilePolicy::CONFIRM_SUCCESS,
    WechatRechargeReconcilePolicy::confirmationStatus([
        'tradeState' => 'SUCCESS',
        'paySuccess' => true,
    ]),
    '微信支付成功必须返回到账成功状态'
);
foreach (['NOTPAY', 'USERPAYING', 'UNKNOWN', ''] as $state) {
    assertWechatRechargeSame(
        WechatRechargeReconcilePolicy::CONFIRM_PROCESSING,
        WechatRechargeReconcilePolicy::confirmationStatus([
            'tradeState' => $state,
            'paySuccess' => false,
        ]),
        '微信非终态结果必须返回确认中，不能误报订单未支付'
    );
}
foreach (['CLOSED', 'REVOKED', 'PAYERROR', 'REFUND'] as $state) {
    assertWechatRechargeSame(
        WechatRechargeReconcilePolicy::CONFIRM_FAILED,
        WechatRechargeReconcilePolicy::confirmationStatus([
            'tradeState' => $state,
            'paySuccess' => false,
        ]),
        '微信明确终态失败才允许返回支付失败状态'
    );
}
assertWechatRechargeSame(
    WechatRechargeReconcilePolicy::ACTION_MANUAL,
    WechatRechargeReconcilePolicy::classify(['tradeState' => 'REFUND', 'paySuccess' => false]),
    '微信退款交易必须转人工核对'
);
assertWechatRechargeSame(300, WechatRechargeReconcilePolicy::retryDelaySeconds(1), '首次重试应等待5分钟');
assertWechatRechargeSame(10800, WechatRechargeReconcilePolicy::retryDelaySeconds(6), '后续重试最长等待3小时');

$wechat = new WechatV3();
$notifyParamsProperty = new ReflectionProperty(WechatV3::class, 'notifyParams');
$notifyParamsProperty->setAccessible(true);
$notifyParamsProperty->setValue($wechat, [
    'transaction_id' => 'WX-TRANSACTION-1',
    'out_trade_no' => 'OUT-TRADE-1',
    'trade_state' => 'SUCCESS',
    'amount' => ['total' => 5000, 'currency' => 'CNY'],
    'mchid' => 'MCH-1',
    'success_time' => '2026-08-11T12:00:00+08:00',
]);
$mappedNotify = $wechat->getNotifyParams();
assertWechatRechargeSame(5000, $mappedNotify['amountTotal'], 'V3通知必须向入账服务传递实付分金额');
assertWechatRechargeSame('SUCCESS', $mappedNotify['tradeState'], 'V3通知必须向入账服务传递微信交易状态');
assertWechatRechargeSame('OUT-TRADE-1', $mappedNotify['outTradeNo'], 'V3通知必须保留商户订单号');

$notifyReflection = new ReflectionClass(NotifyService::class);
$notifyService = $notifyReflection->newInstanceWithoutConstructor();
$merchantMethod = $notifyReflection->getMethod('assertWechatMerchant');
$merchantMethod->setAccessible(true);
$merchantMethod->invoke($notifyService, [
    'mchType' => 'normal',
    'normal' => ['mchId' => 'MCH-1'],
], $mappedNotify);
$merchantMismatchRejected = false;
try {
    $merchantMethod->invoke($notifyService, [
        'mchType' => 'normal',
        'normal' => ['mchId' => 'OTHER-MCH'],
    ], $mappedNotify);
} catch (Throwable $e) {
    $merchantMismatchRejected = strpos($e->getMessage(), '商户号') !== false;
}
assertWechatRechargeSame(true, $merchantMismatchRejected, 'V3通知商户号不一致时必须拒绝入账');

$app = new \think\App();
$app->setEnvName(TP_ENV === 'prod' && BUSINESS !== 'aizhct' ? TP_ENV . BUSINESS : TP_ENV);
$app->initialize();

// Test-only isolated metadata fixture. Original assertions are unchanged.
$upgradeFixtureFile = tempnam(sys_get_temp_dir(), 'upgrade-wechat-');
\think\facade\Config::set([
    'default' => 'upgrade_fixture',
    'connections' => [
        'upgrade_fixture' => [
            'type' => 'sqlite', 'database' => $upgradeFixtureFile,
            'prefix' => 'yoshop_', 'deploy' => 0,
            'break_reconnect' => false, 'trigger_sql' => false,
        ],
    ],
], 'database');
\think\facade\Db::connect()->execute('CREATE TABLE yoshop_recharge_order (order_id INTEGER PRIMARY KEY, pay_price DECIMAL(10,2), create_time INTEGER, update_time INTEGER)');
register_shutdown_function(function () use ($upgradeFixtureFile) { @unlink($upgradeFixtureFile); });

$logConfig = \think\facade\Config::get('log');
$logConfig['channels']['file']['path'] = '/tmp/ai_api_logs/';
\think\facade\Config::set($logConfig, 'log');
\think\App::$store_id = 10001;

$paySuccessReflection = new ReflectionClass(RechargePaySuccess::class);
$paySuccess = $paySuccessReflection->newInstanceWithoutConstructor();
$paymentDataProperty = $paySuccessReflection->getProperty('paymentData');
$paymentDataProperty->setAccessible(true);
$amountMethod = $paySuccessReflection->getMethod('assertPaymentAmount');
$amountMethod->setAccessible(true);
$order = new RechargeOrderModel(['pay_price' => '50.00']);

$paymentDataProperty->setValue($paySuccess, ['amountTotal' => 5000, 'currency' => 'CNY']);
$amountMethod->invoke($paySuccess, $order);
$paymentDataProperty->setValue($paySuccess, ['amountTotal' => 4999, 'currency' => 'CNY']);
$amountMismatchRejected = false;
try {
    $amountMethod->invoke($paySuccess, $order);
} catch (ReflectionException $e) {
    throw $e;
} catch (Throwable $e) {
    $amountMismatchRejected = strpos($e->getMessage(), '金额') !== false;
}
assertWechatRechargeSame(true, $amountMismatchRejected, '微信实付金额不一致时必须拒绝本地入账');
$paymentDataProperty->setValue($paySuccess, ['amountTotal' => null, 'currency' => '']);
$missingAmountRejected = false;
try {
    $amountMethod->invoke($paySuccess, $order);
} catch (Throwable $e) {
    $missingAmountRejected = strpos($e->getMessage(), '金额') !== false;
}
assertWechatRechargeSame(true, $missingAmountRejected, 'V3通知缺少实付金额时必须拒绝本地入账');

assertWechatRechargeContains(
    "return response('', 204)",
    'app/api/controller/Notify.php',
    '微信V3本地入账成功后必须明确返回204'
);
assertWechatRechargeContains(
    '->code(500)',
    'app/api/controller/Notify.php',
    '微信V3本地入账失败必须返回非2xx'
);
assertWechatRechargeContains(
    'throw $e;',
    'app/api/service/Notify.php',
    '微信V3服务异常必须继续向HTTP层传播'
);
assertWechatRechargeContains(
    'assertLocalWalletAllowedByAiUserId',
    'app/api/service/recharge/PaySuccess.php',
    '最终入账前必须再次执行一卡通用户门禁'
);
assertWechatRechargeContains(
    'assertPaymentAmount',
    'app/api/service/recharge/PaySuccess.php',
    '最终入账前必须核对微信实付金额'
);
assertWechatRechargeContains(
    "'WechatRechargeReconcile'",
    'config/console.php',
    '正式充值对账命令必须注册'
);
assertWechatRechargeContains(
    'WechatRechargeReconcile',
    'cron/shell/prod/cron.yml',
    '生产定时任务样例必须交付'
);
assertWechatRechargeContains(
    'CREATE TABLE `yoshop_payment_reconcile`',
    'db/v0.33.0.sql',
    '正式充值对账状态表SQL必须交付'
);
assertWechatRechargeContains(
    "->where('t.create_time', '>=', time() - 72 * 3600)",
    'app/api/service/recharge/WechatRechargeReconciler.php',
    '自动对账不得无限扫描72小时前的历史待支付单'
);
assertWechatRechargeContains(
    'tradeQueryResult($outTradeNo)',
    'app/api/controller/Recharge.php',
    '充值查单接口必须读取微信完整交易状态'
);
assertWechatRechargeContains(
    "'confirmStatus'",
    'app/api/controller/Recharge.php',
    '充值查单接口必须返回到账确认状态'
);
assertWechatRechargeContains(
    '$method === PaymentMethodEnum::WECHAT',
    'app/api/controller/Recharge.php',
    '支付确认中状态只能改变微信查单体验，不得改变其他支付方式原有结果'
);

echo "Wechat recharge reliability tests passed." . PHP_EOL;
