<?php

use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Log;
use Modules\Booking\Models\Booking;
use Modules\Payment\Support\KbzSignature;

/**
 * @param  array<string, mixed>  $overrides
 * @return array<string, mixed>
 */
function signedKbzWebhookBody(array $overrides = []): array
{
    $merchantKey = 'test-merchant-key';

    $notification = array_merge([
        'appid' => 'APPID123',
        'notify_time' => '1576842150',
        'merch_code' => 'MERCH001',
        'merch_order_id' => 'EVB-FIXTURE-001-1',
        'mm_order_id' => '01001814070006560257',
        'trans_currency' => 'MMK',
        'total_amount' => '15000',
        'trade_status' => 'PAY_SUCCESS',
        'trans_end_time' => '1576834704',
        'nonce_str' => '513ba55344ad44c8b69465aae66f7703',
        'sign_type' => 'SHA256',
    ], $overrides);

    $notification['sign'] = KbzSignature::sign($notification, $merchantKey);

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

beforeEach(function () {
    config(['services.kbz.merchant_key' => 'test-merchant-key']);
});

test('a validly signed kbz webhook is acknowledged with the literal success body', function () {
    Log::spy();

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

    Log::shouldHaveReceived('info')->once();
});

test('an invalidly signed kbz webhook is rejected with 400, never 500', function () {
    Log::spy();

    $body = signedKbzWebhookBody();
    $body['Request']['sign'] = 'tampered';

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

    Log::shouldHaveReceived('warning')->once();
});

test('an unknown gateway in the route segment 404s rather than reaching a controller', function () {
    $this->postJson('/api/v1/webhooks/not_a_real_gateway', signedKbzWebhookBody())
        ->assertNotFound();
});

test('a missing signature is rejected with 400', function () {
    $this->postJson('/api/v1/webhooks/kbz_mini_app', ['Request' => ['trade_status' => 'PAY_SUCCESS']])
        ->assertStatus(400);
});

test('the optional encrypted booking id segment is accepted and decrypted for logging', function () {
    Log::spy();

    $booking = Booking::factory()->create();
    $encrypted = Crypt::encryptString((string) $booking->id);

    $this->postJson("/api/v1/webhooks/kbz_mini_app/{$encrypted}", signedKbzWebhookBody())
        ->assertOk();

    Log::shouldHaveReceived('info')->withArgs(
        fn (string $message, array $context): bool => $context['booking_id'] === $booking->id
    )->once();
});

test('a garbage encrypted booking id segment does not fail the webhook', function () {
    $this->postJson('/api/v1/webhooks/kbz_mini_app/not-a-real-ciphertext', signedKbzWebhookBody())
        ->assertOk();
});

test('no auth:sanctum is required to reach the webhook', function () {
    $this->postJson('/api/v1/webhooks/kbz_mini_app', signedKbzWebhookBody())
        ->assertOk();
});
