<?php

namespace App\Services;

use App\Models\InventoryReservation;
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,
        protected LocationInventoryService $locationInventory,
        protected OrderAllocation\OrderAllocationService $allocationService,
    ) {
    }

    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');

        try {
            if ($this->locationInventory->useLocationInventory()) {
                $lines = $order->items->map(fn ($item) => [
                    'product_id' => (int) $item->product_id,
                    'qty' => (int) $item->qty,
                    'order_item_id' => $item->id,
                ])->values()->all();

                $this->allocationService->allocateParity(
                    $order,
                    $lines,
                    $order->fulfillment_type ?: 'home_delivery',
                    $order->preferred_store_id
                );
            } else {
                foreach ($order->items as $item) {
                    $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,
                'error' => $e->getMessage(),
            ]);

            return false;
        }

        return $this->confirmPendingReservations($order->id);
    }

    protected function confirmPendingReservations(int $orderId): bool
    {
        if ($this->locationInventory->useLocationInventory()) {
            $reservations = InventoryReservation::where('order_id', $orderId)
                ->where('status', 'pending')
                ->where('expires_at', '>', now())
                ->get();

            if ($reservations->isEmpty()) {
                return false;
            }

            foreach ($reservations as $reservation) {
                $this->locationInventory->confirmReservation($reservation->reservation_token);
            }

            return true;
        }

        $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;
    }
}
