<?php

namespace Modules\Payment\Actions;

use Illuminate\Support\Facades\DB;
use Modules\Payment\Enums\PaymentMethod;
use Modules\Payment\Enums\PaymentStatus;
use Modules\Payment\Events\PaymentCompleted;
use Modules\Payment\Events\PaymentFailed;
use Modules\Payment\Models\Payment;
use Modules\Payment\Services\PaymentService;

/**
 * Confirms a Payment following an inbound webhook notification — never
 * trusts the webhook's own trade_status directly, re-verifies with the
 * gateway first (domain.md §6, bnf_event's client-driven-confirmation
 * fallback pattern).
 *
 * Idempotent: KBZ may redeliver the same notification (or this may run more
 * than once for the same transaction for other reasons), so a Payment only
 * ever transitions out of `pending` once — a redelivery after that is a
 * no-op that doesn't re-call the gateway or re-dispatch events.
 */
class ConfirmPaymentAction
{
    public function __construct(
        private PaymentService $paymentService,
    ) {}

    public function handle(PaymentMethod $method, string $gatewayTransactionId): ?Payment
    {
        $payment = Payment::where('gateway', $method)
            ->where('gateway_transaction_id', $gatewayTransactionId)
            ->first();

        if ($payment === null || $payment->status !== PaymentStatus::Pending) {
            return $payment;
        }

        $verified = $this->paymentService->verify($method, $gatewayTransactionId);

        if ($verified->status === PaymentStatus::Pending) {
            return $payment;
        }

        return DB::transaction(function () use ($payment, $verified) {
            $payment->update([
                'status' => $verified->status,
                'gateway_payload' => $verified->gatewayPayload,
                'completed_at' => now(),
            ]);

            match ($verified->status) {
                PaymentStatus::Completed => PaymentCompleted::dispatch($payment),
                PaymentStatus::Failed => PaymentFailed::dispatch($payment),
                PaymentStatus::Pending => null,
            };

            return $payment;
        });
    }
}
