<?php

use Modules\Booking\Enums\BookingStatus;
use Modules\Booking\Models\Booking;
use Modules\Payment\Data\PaymentResultData;
use Modules\Payment\Enums\PaymentMethod;
use Modules\Payment\Enums\PaymentStatus;
use Modules\Payment\Factories\PaymentGatewayFactory;
use Modules\Payment\Gateways\KbzMiniAppGateway;
use Modules\Payment\Models\Payment;
use Modules\Payment\Support\KbzSignature;

/**
 * Keeps KbzMiniAppGateway's real handleWebhook() (so the inbound signature
 * check in the webhook route stays genuine end-to-end) but stubs verify()
 * so the double-delivery/idempotency test never hits the real KBZ API and
 * can count re-verification calls.
 */
class FakeVerifyingKbzGateway extends KbzMiniAppGateway
{
    public static int $verifyCallCount = 0;

    public function verify(string $gatewayTransactionId): PaymentResultData
    {
        self::$verifyCallCount++;

        return new PaymentResultData(
            status: PaymentStatus::Completed,
            gatewayTransactionId: $gatewayTransactionId,
            gatewayPayload: ['trade_status' => 'PAY_SUCCESS'],
        );
    }
}

beforeEach(function () {
    FakeVerifyingKbzGateway::$verifyCallCount = 0;

    config(['services.kbz.merchant_key' => 'test-merchant-key']);
    app(PaymentGatewayFactory::class)->register(PaymentMethod::KbzMiniApp, FakeVerifyingKbzGateway::class);
});

/**
 * @return array<string, mixed>
 */
function signedKbzConfirmationBody(string $merchOrderId): array
{
    $notification = [
        'appid' => 'APPID123',
        'notify_time' => '1576842150',
        'merch_code' => 'MERCH001',
        'merch_order_id' => $merchOrderId,
        'mm_order_id' => '01001814070006560257',
        'trans_currency' => 'MMK',
        'total_amount' => '15000',
        'trade_status' => 'PAY_SUCCESS',
        'trans_end_time' => '1576834704',
        'nonce_str' => '513ba55344ad44c8b69465aae66f7703',
        'sign_type' => 'SHA256',
    ];

    $notification['sign'] = KbzSignature::sign($notification, 'test-merchant-key');

    return ['Request' => $notification];
}

test('a double-delivered kbz webhook only confirms the payment and its booking once', function () {
    $booking = Booking::factory()->create(['status' => BookingStatus::PendingPayment]);
    $payment = Payment::factory()->create([
        'booking_id' => $booking->id,
        'status' => PaymentStatus::Pending,
        'gateway' => PaymentMethod::KbzMiniApp,
        'gateway_transaction_id' => 'EVB-DUPTEST-1',
    ]);

    $body = signedKbzConfirmationBody('EVB-DUPTEST-1');

    $this->postJson('/api/v1/webhooks/kbz_mini_app', $body)->assertOk();

    expect($payment->refresh()->status)->toBe(PaymentStatus::Completed)
        ->and($booking->refresh()->status)->toBe(BookingStatus::Confirmed)
        ->and(FakeVerifyingKbzGateway::$verifyCallCount)->toBe(1);

    // KBZ redelivers the same notification — must not re-verify or re-confirm.
    $this->postJson('/api/v1/webhooks/kbz_mini_app', $body)->assertOk();

    expect($payment->refresh()->status)->toBe(PaymentStatus::Completed)
        ->and($booking->refresh()->status)->toBe(BookingStatus::Confirmed)
        ->and(FakeVerifyingKbzGateway::$verifyCallCount)->toBe(1);
});
