<?php

namespace App\Http\Controllers;

use App\Mail\OrderPlacedNotification;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\SiteSetting;
use App\Services\CartService;
use Illuminate\Database\QueryException;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;

class CheckoutController extends Controller
{
    public function __construct(private CartService $cartService) {}

    public function index()
    {
        $cart = $this->cartService->currentCart(false);

        if (! $cart || $cart->items->isEmpty()) {
            return redirect()->route('cart.index')->with('error', 'Your cart is empty.');
        }

        $shippingFee = $this->shippingFeeFor($cart->total);
        $freeShippingThreshold = $this->freeShippingThreshold();
        $user = Auth::user();

        return view('checkout.index', compact('cart', 'shippingFee', 'freeShippingThreshold', 'user'));
    }

    public function store(Request $request): RedirectResponse
    {
        $cart = $this->cartService->currentCart(false);

        if (! $cart || $cart->items->isEmpty()) {
            return redirect()->route('cart.index')->with('error', 'Your cart is empty.');
        }

        $data = $request->validate([
            'full_name' => ['required', 'string', 'max:255'],
            'email' => ['nullable', 'email', 'max:255'],
            'phone' => ['required', 'string', 'max:30'],
            'address_line1' => ['required', 'string', 'max:255'],
            'address_line2' => ['nullable', 'string', 'max:255'],
            'city' => ['required', 'string', 'max:100'],
            'state' => ['nullable', 'string', 'max:100'],
            'postal_code' => ['nullable', 'string', 'max:20'],
            'notes' => ['nullable', 'string', 'max:1000'],
        ]);

        foreach ($cart->items as $item) {
            if (! $item->product || ! $item->product->is_active) {
                return back()->withInput()->with('error', "'{$item->product?->name}' is no longer available.");
            }

            if ($item->product->has_variants && ! $item->productVariant) {
                return back()->withInput()->with('error', "Please choose an option for '{$item->product->name}'.");
            }

            if (! $item->isPurchasable()) {
                $name = $item->product->name.($item->variantLabel() ? " ({$item->variantLabel()})" : '');

                return back()->withInput()->with('error', "'{$name}' does not have enough stock available.");
            }
        }

        $subtotal = $cart->total;
        $shippingFee = $this->shippingFeeFor($subtotal);

        $order = $this->createOrder($data, $cart, $subtotal, $shippingFee);

        $this->notifyStoreOwner($order);

        return redirect()->route('checkout.confirmation', $order)->with('success', 'Your order has been placed!');
    }

    public function confirmation(Order $order)
    {
        if (Auth::check()) {
            abort_unless($order->user_id === Auth::id(), 403);
        }

        $order->load('items');

        return view('checkout.confirmation', compact('order'));
    }

    /**
     * Persist the order + items and adjust stock. Retries once if the generated
     * sequential order number collides with a concurrent checkout.
     */
    private function createOrder(array $data, $cart, float $subtotal, float $shippingFee): Order
    {
        $attempts = 0;

        while (true) {
            try {
                return DB::transaction(function () use ($data, $cart, $subtotal, $shippingFee) {
                    $order = Order::create($data + [
                        'user_id' => Auth::id(),
                        'subtotal' => $subtotal,
                        'shipping_fee' => $shippingFee,
                        'total' => $subtotal + $shippingFee,
                        'payment_method' => 'cod',
                        'status' => 'pending',
                    ]);

                    foreach ($cart->items as $item) {
                        $variant = $item->productVariant;
                        $unitPrice = $item->unitPrice();
                        $image = $variant?->images->first()?->image ?? $item->product->image;

                        OrderItem::create([
                            'order_id' => $order->id,
                            'product_id' => $item->product_id,
                            'product_variant_id' => $item->product_variant_id,
                            'product_name' => $item->product->name,
                            'variant_label' => $variant?->label,
                            'product_image' => $image,
                            'price' => $unitPrice,
                            'quantity' => $item->quantity,
                            'line_total' => $item->quantity * $unitPrice,
                        ]);

                        $variant?->decrement('stock_quantity', $item->quantity);
                        // Keep the aggregate product stock in sync with its variants.
                        $item->product->decrement('stock_quantity', $item->quantity);
                    }

                    $cart->items()->delete();

                    return $order;
                });
            } catch (QueryException $e) {
                // 23000 = integrity constraint violation (duplicate order_number).
                if ($e->getCode() === '23000' && $attempts++ < 3) {
                    continue;
                }

                throw $e;
            }
        }
    }

    private function notifyStoreOwner(Order $order): void
    {
        $recipient = SiteSetting::get('contact_email');

        if (! $recipient || ! filter_var($recipient, FILTER_VALIDATE_EMAIL)) {
            return;
        }

        try {
            Mail::to($recipient)->send(new OrderPlacedNotification($order->load('items')));
        } catch (\Throwable $e) {
            // A mail failure must never break a completed checkout.
            report($e);
        }
    }

    private function shippingFeeFor(float $subtotal): float
    {
        $threshold = $this->freeShippingThreshold();

        if ($threshold > 0 && $subtotal >= $threshold) {
            return 0.0;
        }

        return (float) SiteSetting::get('shipping_fee', 0);
    }

    private function freeShippingThreshold(): float
    {
        return (float) SiteSetting::get('free_shipping_threshold', 5000);
    }
}
