<?php

namespace Tests\Feature\Webhooks;

use App\Models\Payment;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class RazorpayWebhookTest extends TestCase
{
    use RefreshDatabase;

    public function test_captured_webhook_marks_payment_captured_and_unlocks_user(): void
    {
        config([
            'services.razorpay.key' => 'rzp_test_key',
            'services.razorpay.secret' => 'test_secret',
            'services.razorpay.webhook_secret' => 'webhook_secret',
        ]);

        $user = User::factory()->create(['access_unlocked_at' => null]);
        $payment = Payment::query()->create([
            'user_id' => $user->id,
            'provider' => 'razorpay',
            'razorpay_order_id' => 'order_webhook123',
            'amount_paise' => 199900,
            'currency' => 'INR',
            'status' => Payment::STATUS_CREATED,
        ]);

        $payload = $this->payload(
            orderId: $payment->razorpay_order_id,
            paymentId: 'pay_webhook123',
            status: 'captured',
            amount: 199900,
            currency: 'INR',
        );
        $rawPayload = json_encode($payload, JSON_THROW_ON_ERROR);

        $response = $this->call(
            'POST',
            route('webhooks.razorpay'),
            [],
            [],
            [],
            [
                'CONTENT_TYPE' => 'application/json',
                'HTTP_X_RAZORPAY_SIGNATURE' => hash_hmac('sha256', $rawPayload, 'webhook_secret'),
            ],
            $rawPayload,
        );

        $response
            ->assertOk()
            ->assertJson(['message' => 'Payment captured.']);

        $payment->refresh();

        $this->assertSame(Payment::STATUS_CAPTURED, $payment->status);
        $this->assertSame('pay_webhook123', $payment->razorpay_payment_id);
        $this->assertNotNull($payment->payment_payload);
        $this->assertNotNull($payment->webhook_payload);
        $this->assertNotNull($payment->verified_at);
        $this->assertNotNull($payment->unlocked_at);
        $this->assertNotNull($user->fresh()->access_unlocked_at);
    }

    public function test_invalid_webhook_signature_is_rejected_without_changing_payment(): void
    {
        config([
            'services.razorpay.key' => 'rzp_test_key',
            'services.razorpay.secret' => 'test_secret',
            'services.razorpay.webhook_secret' => 'webhook_secret',
        ]);

        $user = User::factory()->create(['access_unlocked_at' => null]);
        $payment = Payment::query()->create([
            'user_id' => $user->id,
            'provider' => 'razorpay',
            'razorpay_order_id' => 'order_invalidsig',
            'amount_paise' => 199900,
            'currency' => 'INR',
            'status' => Payment::STATUS_CREATED,
        ]);

        $rawPayload = json_encode($this->payload(
            orderId: $payment->razorpay_order_id,
            paymentId: 'pay_invalidsig',
            status: 'captured',
            amount: 199900,
            currency: 'INR',
        ), JSON_THROW_ON_ERROR);

        $response = $this->call(
            'POST',
            route('webhooks.razorpay'),
            [],
            [],
            [],
            [
                'CONTENT_TYPE' => 'application/json',
                'HTTP_X_RAZORPAY_SIGNATURE' => str_repeat('0', 64),
            ],
            $rawPayload,
        );

        $response
            ->assertStatus(422)
            ->assertJson(['message' => 'Invalid webhook signature.']);

        $payment->refresh();

        $this->assertSame(Payment::STATUS_CREATED, $payment->status);
        $this->assertNull($payment->razorpay_payment_id);
        $this->assertNull($payment->verified_at);
        $this->assertNull($payment->unlocked_at);
        $this->assertNull($user->fresh()->access_unlocked_at);
    }

    /**
     * @return array<string, mixed>
     */
    private function payload(
        string $orderId,
        string $paymentId,
        string $status,
        int $amount,
        string $currency,
    ): array {
        return [
            'event' => 'payment.captured',
            'payload' => [
                'payment' => [
                    'entity' => [
                        'id' => $paymentId,
                        'order_id' => $orderId,
                        'status' => $status,
                        'amount' => $amount,
                        'currency' => $currency,
                    ],
                ],
            ],
        ];
    }
}
