<?php

namespace App\Http\Controllers;

use App\Models\Cart;
use App\Models\Otp;
use App\Models\OtpRateLimit;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Product;
use App\Models\User;
use App\Http\Controllers\Traits\LimitsOtpRequests;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\RateLimiter;

class CheckoutController extends Controller
{
    use LimitsOtpRequests;
    /**
     * Display checkout page.
     */
    public function index()
    {
        $data = $this->getCartData();

        if (! $data) {
            return redirect()->route('shop.cart')->with('error', 'Your cart is empty.');
        }

        return view('shop.checkout', $data);
    }

    /**
     * Display the ultra-fast, minimal express checkout (live sale flow).
     */
    public function expressCheckout()
    {
        $data = $this->getCartData();

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

        $localUser = [];
        try {
            $stored = session()->get('shopie_user_data');
            if (is_string($stored)) {
                $stored = json_decode($stored, true);
            }
            if (is_array($stored)) {
                $localUser = $stored;
            }
        } catch (\Throwable $e) {
            $localUser = [];
        }

        $authUser = auth()->user();

        return view('shop.express-checkout', array_merge($data, compact('localUser', 'authUser')));
    }

    /**
     * Gather cart items, total, and count for the current user/session.
     * Returns null when the cart is empty.
     */
    private function getCartData()
    {
        if (auth()->check()) {
            $cartItems = Cart::with('product.category')
                ->where('user_id', auth()->id())
                ->get()
                ->map(function ($item) {
                    $variant = $item->variant ?? $item->product->resolveVariantByIndex($item->variant_index);

                    return [
                        'product' => $item->product,
                        'quantity' => $item->quantity,
                        'price' => $item->price ?? $item->product->price,
                        'services' => $item->services ?? [],
                        'variant' => $variant,
                        'variant_index' => $item->variant_index,
                    ];
                });
        } else {
            $sessionCart = session()->get('cart', []);
            $cartItems = collect();

            foreach ($sessionCart as $item) {
                $product = Product::with('category')->find($item['product_id']);
                if ($product) {
                    $variant = $item['variant'] ?? $product->resolveVariantByIndex($item['variant_index'] ?? null);

                    $cartItems->push([
                        'product' => $product,
                        'quantity' => $item['quantity'],
                        'price' => $item['price'] ?? ($variant['final_price'] ?? $product->price),
                        'services' => $item['services'] ?? [],
                        'variant' => $variant,
                        'variant_index' => $item['variant_index'] ?? null,
                    ]);
                }
            }
        }

        if ($cartItems->isEmpty()) {
            return null;
        }

        $subtotal = $cartItems->sum(function ($item) {
            return $item['price'] * $item['quantity'];
        });

        $servicesTotal = $cartItems->sum(function ($item) {
            return collect($item['services'])->sum(fn ($s) => $s['resolved_price'] * $item['quantity']);
        });

        $cartCount = $cartItems->sum('quantity');

        return compact('cartItems', 'subtotal', 'servicesTotal', 'cartCount');
    }

    /**
     * Process checkout (alias for store method).
     */
    public function process(Request $request)
    {
        try {
            return $this->store($request);
        } catch (\Exception $e) {
            \Log::error('Error in process method:', ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]);

            return redirect()->back()->with('error', 'Something went wrong. Please try again.');
        }
    }

    /**
     * Process checkout and create order.
     */
    public function store(Request $request)
    {
        \Log::info('CheckoutController store method called', $request->all());

        try {
            $request->validate([
                'name' => 'required|string|max:255',
                'phone' => 'required|string|max:15',
                'address' => 'required|string',
                'shipping_area' => 'nullable|string',
                'note' => 'nullable|string',
            ]);
        } catch (\Illuminate\Validation\ValidationException $e) {
            \Log::error('Validation failed in checkout:', $e->errors());

            return redirect()->back()->withErrors($e->errors())->withInput();
        } catch (\Exception $e) {
            \Log::error('Unexpected error during validation:', ['error' => $e->getMessage()]);

            return redirect()->back()->with('error', 'Something went wrong. Please try again.');
        }

        DB::beginTransaction();

        try {
            // Get cart items
            if (auth()->check()) {
                $cartItems = Cart::with('product.category')
                    ->where('user_id', auth()->id())
                    ->get()
                    ->map(function ($item) {
                        $variant = $item->variant ?? $item->product->resolveVariantByIndex($item->variant_index);

                        return [
                            'product' => $item->product,
                            'quantity' => $item['quantity'],
                            'price' => $item->price ?? $item->product->price,
                            'services' => $item->services ?? [],
                            'variant' => $variant,
                            'variant_index' => $item->variant_index,
                        ];
                    });
            } else {
                $sessionCart = session()->get('cart', []);
                $cartItems = collect();

                foreach ($sessionCart as $item) {
                    $product = Product::with('category')->find($item['product_id']);
                    if ($product) {
                        $variant = $item['variant'] ?? $product->resolveVariantByIndex($item['variant_index'] ?? null);

                        $cartItems->push([
                            'product' => $product,
                            'quantity' => $item['quantity'],
                            'price' => $item['price'] ?? ($variant['final_price'] ?? $product->price),
                            'services' => $item['services'] ?? [],
                            'variant' => $variant,
                            'variant_index' => $item['variant_index'] ?? null,
                        ]);
                    }
                }
            }

            if ($cartItems->isEmpty()) {
                return redirect()->route('shop.cart')->with('error', 'Your cart is empty.');
            }

            // Calculate totals
            $subtotal = $cartItems->sum(function ($item) {
                return $item['price'] * $item['quantity'];
            });

            $servicesTotal = $cartItems->sum(function ($item) {
                return collect($item['services'])->sum(fn ($s) => $s['resolved_price'] * $item['quantity']);
            });

            // Calculate delivery fee based on settings
            $deliveryFee = calculate_delivery_fee($subtotal);

            // Create order
            $order = Order::create([
                'user_id' => auth()->id(),
                'order_number' => 'ORD-'.strtoupper(uniqid()),
                'customer_name' => $request->name,
                'customer_phone' => $request->phone,
                'customer_address' => $request->address.($request->shipping_area ? ' ('.$request->shipping_area.')' : ''),
                'subtotal' => $subtotal,
                'services_total' => $servicesTotal,
                'shipping_amount' => $deliveryFee,
                'total_amount' => $subtotal + $servicesTotal + $deliveryFee,
                'status' => 'pending',
                'payment_status' => 'pending',
                'payment_method' => $request->payment_method ?? 'cash_on_delivery',
                'notes' => $request->note,
            ]);

            // Create order items
            foreach ($cartItems as $item) {
                OrderItem::create([
                    'order_id' => $order->id,
                    'product_id' => $item['product']->id,
                    'product_name' => $item['product']->name,
                    'quantity' => $item['quantity'],
                    'price' => $item['price'],
                    'total' => $item['price'] * $item['quantity'],
                    'product_meta' => [
                        'services' => $item['services'],
                        'variant' => $item['variant'] ?? null,
                    ],
                ]);
            }

            // Clear cart
            if (auth()->check()) {
                Cart::where('user_id', auth()->id())->delete();
            } else {
                session()->forget('cart');
            }

            // Persist customer data to localStorage via session flag
            session()->flash('checkout_user_data', [
                'name' => $request->name,
                'phone' => $request->phone,
                'address' => $request->address,
            ]);

            DB::commit();

            return redirect()->route('order.success', $order->order_number)
                ->with('success', 'Order placed successfully!');

        } catch (\Exception $e) {
            \Log::error('Error creating order:', ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]);
            DB::rollback();

            return redirect()->back()
                ->withErrors(['error' => 'Something went wrong. Please try again.'])
                ->withInput();
        }
    }

    /**
     * Show order success page.
     */
    public function success($orderNumber)
    {
        $order = Order::with(['orderItems.product', 'user'])
            ->where('order_number', $orderNumber)
            ->firstOrFail();

        return view('shop.order-success', compact('order'));
    }

    /**
     * Send 4-digit OTP for checkout verification.
     */
    public function sendCheckoutOtp(Request $request): JsonResponse
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'phone' => 'required|string|max:15|regex:/^01[3-9][0-9]{8}$/',
            'address' => 'required|string',
            'shipping_area' => 'nullable|string',
            'note' => 'nullable|string',
            'payment_method' => 'required|string|in:cash_on_delivery,bkash,nagad,bank_transfer',
        ]);

        $ip = $this->resolveClientIp();
        $phone = $request->phone;

        $rateLimited = $this->otpRateLimitExceeded($ip, $phone, 'checkout');
        if ($rateLimited) {
            return $rateLimited;
        }

        $this->recordOtpAttempt($ip, $phone);

        try {
            $otp = Otp::createForPhone($phone, 'checkout_verification', 4, 5);

            session(['checkout_data' => [
                'name' => $request->name,
                'phone' => $phone,
                'address' => $request->address,
                'shipping_area' => $request->shipping_area,
                'note' => $request->note,
                'payment_method' => $request->payment_method,
            ]]);

            \Log::info("Checkout OTP for {$phone}: {$otp->otp_code}");

            return response()->json([
                'success' => true,
                'message' => 'Verification code sent to your phone.',
            ]);
        } catch (\Exception $e) {
            \Log::error('Failed to send checkout OTP:', ['error' => $e->getMessage()]);

            return response()->json([
                'success' => false,
                'message' => 'Failed to send verification code. Please try again.',
            ], 500);
        }
    }

    /**
     * Verify OTP and log the user in for checkout.
     */
    public function verifyCheckoutOtp(Request $request): JsonResponse
    {
        $request->validate([
            'phone' => 'required|string',
            'otp' => 'required|string|size:4',
        ]);

        $otp = Otp::where('phone', $request->phone)
            ->where('otp_code', $request->otp)
            ->where('type', 'checkout_verification')
            ->where('is_verified', false)
            ->where('expires_at', '>', now())
            ->first();

        if (! $otp) {
            return response()->json([
                'success' => false,
                'message' => 'Invalid or expired verification code.',
            ], 422);
        }

        $checkoutData = session('checkout_data');
        if (! $checkoutData) {
            return response()->json([
                'success' => false,
                'message' => 'Checkout session expired. Please try again.',
            ], 422);
        }

        $otp->update(['is_verified' => true]);

        $this->resetOtpRateLimits($this->resolveClientIp(), $checkoutData['phone']);

        try {
            $user = User::where('phone', $checkoutData['phone'])->first();
            if ($user) {
                $user->update([
                    'name' => $checkoutData['name'],
                    'address' => $checkoutData['address'],
                    'phone_verified_at' => now(),
                    'password' => bcrypt($request->otp),
                ]);
            } else {
                $user = User::create([
                    'name' => $checkoutData['name'],
                    'phone' => $checkoutData['phone'],
                    'address' => $checkoutData['address'],
                    'phone_verified_at' => now(),
                    'password' => bcrypt($request->otp),
                ]);
            }

            Auth::login($user);
            $request->session()->regenerate();

            $cartController = new CartController();
            $cartController->transferSessionCartToDatabase($user->id);

            return response()->json([
                'success' => true,
                'message' => 'Login successful. Place the order to continue.',
                'user_data' => [
                    'name' => $checkoutData['name'],
                    'phone' => $checkoutData['phone'],
                    'address' => $checkoutData['address'],
                ],
            ]);
        } catch (\Exception $e) {
            \Log::error('Error in checkout OTP verify:', ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]);

            return response()->json([
                'success' => false,
                'message' => 'Something went wrong. Please try again.',
            ], 500);
        }
    }

    /**
     * Lookup user address by phone number.
     */
    public function getUserAddress(Request $request): JsonResponse
    {
        $request->validate([
            'phone' => 'required|string|regex:/^01[3-9][0-9]{8}$/',
        ]);

        $user = User::where('phone', $request->phone)
            ->whereNotNull('address')
            ->orderByDesc('created_at')
            ->first();

        if (! $user) {
            return response()->json([
                'success' => false,
                'message' => 'No saved address found for this phone number.',
            ], 404);
        }

        return response()->json([
            'success' => true,
            'name' => $user->name,
            'address' => $user->address,
        ]);
    }

    /**
     * Send OTP for guest checkout verification.
     */
    public function sendOtp(Request $request): JsonResponse
    {
        $request->validate([
            'phone' => 'required|string|max:15',
        ]);

        $phone = $request->phone;
        $ip = $this->resolveClientIp();

        $rateLimited = $this->otpRateLimitExceeded($ip, $phone, 'checkout_legacy');
        if ($rateLimited) {
            return $rateLimited;
        }

        $this->recordOtpAttempt($ip, $phone);

        try {
            Otp::where('phone', $phone)->where('type', 'checkout_verification')->delete();

            $otpCode = sprintf('%06d', mt_rand(100000, 999999));

            Otp::create([
                'phone' => $phone,
                'otp_code' => $otpCode,
                'type' => 'checkout_verification',
                'is_verified' => false,
                'expires_at' => now()->addMinutes(5),
            ]);

            \Log::info("Checkout OTP (legacy) for {$phone}: {$otpCode}");

            return response()->json([
                'success' => true,
                'message' => 'Verification code sent to your phone.',
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Failed to send verification code. Please try again.',
            ], 500);
        }
    }

    /**
     * Verify OTP for guest checkout.
     */
    public function verifyOtp(Request $request): JsonResponse
    {
        $request->validate([
            'phone' => 'required|string',
            'otp' => 'required|string|size:6',
        ]);

        $otp = Otp::where('phone', $request->phone)
            ->where('otp_code', $request->otp)
            ->where('type', 'checkout_verification')
            ->where('is_verified', false)
            ->where('expires_at', '>', now())
            ->first();

        if (! $otp) {
            return response()->json([
                'success' => false,
                'message' => 'Invalid or expired verification code.',
            ], 422);
        }

        $otp->update(['is_verified' => true]);

        $this->resetOtpRateLimits($this->resolveClientIp(), $request->phone);

        return response()->json([
            'success' => true,
            'message' => 'Phone number verified successfully.',
        ]);
    }

    /**
     * Track order by order number and phone.
     */
    public function trackOrder(Request $request)
    {
        $request->validate([
            'order_number' => 'required|string',
            'phone' => 'required|string',
        ]);

        $order = Order::with(['orderItems.product', 'user'])
            ->where('order_number', $request->order_number)
            ->where('customer_phone', $request->phone)
            ->first();

        if (! $order) {
            return redirect()->back()
                ->withErrors(['error' => 'Order not found. Please check your order number and phone number.']);
        }

        return view('shop.track-order', compact('order'));
    }

    /**
     * Show OTP verification page for order.
     */
    public function showOrderOtp($orderNumber)
    {
        $order = Order::where('order_number', $orderNumber)->firstOrFail();

        return view('shop.verify-order-otp', compact('order'));
    }

    /**
     * Verify OTP for order.
     */
    public function verifyOrderOtp(Request $request, $orderNumber)
    {
        $request->validate([
            'otp' => 'required|string|size:6',
        ]);

        $order = Order::where('order_number', $orderNumber)->firstOrFail();

        $otp = Otp::where('phone', $order->customer_phone)
            ->where('otp_code', $request->otp)
            ->where('type', 'order_verification')
            ->where('expires_at', '>', now())
            ->first();

        if (! $otp) {
            return redirect()->back()
                ->withErrors(['otp' => 'Invalid or expired verification code.']);
        }

        $otp->update(['is_verified' => true]);

        $order->update(['status' => 'confirmed']);

        return redirect()->route('order.success', $order->order_number)
            ->with('success', 'Phone number verified successfully!');
    }

    /**
     * Resend OTP for order verification.
     */
    public function resendOrderOtp($orderNumber)
    {
        $order = Order::where('order_number', $orderNumber)->firstOrFail();

        $ip = $this->resolveClientIp();
        $phone = $order->customer_phone;

        $rateLimited = $this->otpRateLimitExceeded($ip, $phone, 'order_verification');
        if ($rateLimited) {
            return redirect()->back()->withErrors(['error' => 'Too many verification requests. Please wait before trying again.']);
        }

        $this->recordOtpAttempt($ip, $phone);

        try {
            $otp = Otp::createForPhone($order->customer_phone, 'order_verification', 6, 5);

            \Log::info("Order OTP for {$order->customer_phone}: {$otp->otp_code}");

            return redirect()->back()
                ->with('success', 'Verification code sent to your phone.');
        } catch (\Exception $e) {
            return redirect()->back()
                ->withErrors(['error' => 'Failed to send verification code. Please try again.']);
        }
    }
}
