<?php

namespace App\Services;

use App\Models\Order;
use App\Models\StockReservation;
use App\Support\PaymentLogger;
use Illuminate\Support\Facades\Log;

class CapturedPaymentRecoveryService
{
    public function __construct(
        protected RazorpayService $razorpayService,
        protected InventoryService $inventoryService
    ) {
    }

    public function isGatewayPaymentCaptured(string $paymentId): bool
    {
        if ($paymentId === '') {
            return false;
        }

        $state = $this->razorpayService->resolvePaymentState($paymentId);

        return ($state['status'] ?? RazorpayService::PAYMENT_STATUS_UNKNOWN) === RazorpayService::PAYMENT_STATUS_CAPTURED;
    }

    /**
     * Resolve a captured Razorpay payment id from the order row or linked Razorpay order.
     */
    public function resolveCapturedPaymentId(Order $order): ?string
    {
        $paymentId = (string) ($order->razorpay_payment_id ?? '');
        if ($paymentId !== '' && $this->isGatewayPaymentCaptured($paymentId)) {
            return $paymentId;
        }

        $razorpayOrderId = (string) ($order->razorpay_order_id ?? '');
        if ($razorpayOrderId === '') {
            return null;
        }

        $fromGateway = $this->razorpayService->findCapturedPaymentIdForRazorpayOrder($razorpayOrderId);

        return $fromGateway !== null && $fromGateway !== '' ? $fromGateway : null;
    }

    /**
     * Mark order paid when Razorpay already captured funds (late webhook / race recovery).
     */
    public function recoverCapturedPayment(Order $order, string $paymentId, string $source): bool
    {
        if ($paymentId === '') {
            return false;
        }

        $order = Order::whereKey($order->id)->lockForUpdate()->firstOrFail();

        if ($this->isTerminalPaid($order)) {
            return true;
        }

        if (!$this->isGatewayPaymentCaptured($paymentId)) {
            return false;
        }

        $previousOrderStatus = $order->status;
        $previousPaymentStatus = $order->payment_status;

        $stockConfirmed = $this->tryConfirmStockForOrder($order);

        $order->update([
            'razorpay_payment_id' => $paymentId,
            'payment_status' => Order::PAYMENT_PAID,
            'payment_method' => 'online',
            'status' => Order::STATUS_PLACED,
            'placed_at' => $order->placed_at ?? now(),
        ]);

        PaymentLogger::warning('Payment recovered from captured gateway state', [
            'order_id' => $order->id,
            'payment_id' => $paymentId,
            'source' => $source,
            'stock_confirmed' => $stockConfirmed,
            'previous_order_status' => $previousOrderStatus,
            'previous_payment_status' => $previousPaymentStatus,
            'failure_bucket' => 'backend_state_race',
        ]);

        if (!$stockConfirmed) {
            Log::warning('Captured payment recovered without active stock reservation', [
                'order_id' => $order->id,
                'payment_id' => $paymentId,
                'source' => $source,
            ]);
        }

        return true;
    }

    /**
     * Confirm pending reservations, or re-reserve order lines then confirm.
     */
    public function tryConfirmStockForOrder(Order $order): bool
    {
        if ($this->confirmPendingReservations($order->id)) {
            return true;
        }

        $order->loadMissing('items');

        foreach ($order->items as $item) {
            try {
                $this->inventoryService->reserve(
                    $item->product_id,
                    $item->qty,
                    null,
                    $order->id
                );
            } catch (\Throwable $e) {
                Log::warning('Could not re-reserve stock during captured payment recovery', [
                    'order_id' => $order->id,
                    'product_id' => $item->product_id,
                    'error' => $e->getMessage(),
                ]);

                return false;
            }
        }

        return $this->confirmPendingReservations($order->id);
    }

    protected function confirmPendingReservations(int $orderId): bool
    {
        $reservations = StockReservation::where('order_id', $orderId)
            ->where('status', 'pending')
            ->where('expires_at', '>', now())
            ->get();

        if ($reservations->isEmpty()) {
            return false;
        }

        foreach ($reservations as $reservation) {
            $this->inventoryService->confirmReservation($reservation->reservation_token);
        }

        return true;
    }

    protected function isTerminalPaid(Order $order): bool
    {
        return $order->payment_status === Order::PAYMENT_PAID
            && $order->status === Order::STATUS_PLACED;
    }
}
