<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Cart;
use App\Models\Coupon;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\OrderHistory;
use App\Models\Product;
use App\Models\ProductVariant;
use App\Models\ShippingMethod;
use App\Models\Setting;
use App\Models\BdDivision;
use App\Models\BdDistrict;
use App\Models\BdUpazila;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Exception;

class CheckoutApiController extends Controller
{
    /**
     * Helper to get cart.
     */
    private function getCart(Request $request)
    {
        if (Auth::check()) {
            return Cart::firstOrCreate(['user_id' => Auth::id()]);
        }
        $guestToken = $request->header('X-Guest-Cart-Token');
        if (!$guestToken) {
            $guestToken = $request->session()->getId();
        }
        return Cart::firstOrCreate(['session_id' => $guestToken]);
    }

    /**
     * Get list of divisions.
     */
    public function divisions()
    {
        return response()->json(BdDivision::all());
    }

    /**
     * Get all districts.
     */
    public function allDistricts()
    {
        return response()->json(\App\Models\BdDistrict::all());
    }

    /**
     * Get districts for a division.
     */
    public function districts($divisionId)
    {
        return response()->json(BdDistrict::where('division_id', $divisionId)->get());
    }

    /**
     * Get upazilas for a district.
     */
    public function upazilas($districtId)
    {
        return response()->json(BdUpazila::where('district_id', $districtId)->get());
    }

    /**
     * Validate and apply coupon.
     */
    public function applyCoupon(Request $request)
    {
        $request->validate([
            'coupon_code' => 'required|string',
        ]);

        $cart = $this->getCart($request);
        
        // Calculate subtotal
        $subtotal = 0.00;
        foreach ($cart->items as $item) {
            $price = $item->variant && $item->variant->price !== null && floatval($item->variant->price) > 0
                ? $item->variant->price 
                : $item->product->price;
            $subtotal += $price * $item->quantity;
        }

        if ($subtotal <= 0) {
            return response()->json(['success' => false, 'message' => 'Cart is empty.'], 422);
        }

        $coupon = Coupon::where('code', strtoupper(trim($request->coupon_code)))
            ->where('is_active', true)
            ->where(function($q) {
                $q->whereNull('starts_at')->orWhere('starts_at', '<=', now());
            })
            ->where(function($q) {
                $q->whereNull('expires_at')->orWhere('expires_at', '>=', now());
            })
            ->first();

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

        if ($subtotal < $coupon->min_amount) {
            return response()->json([
                'success' => false, 
                'message' => 'Minimum spend of ৳' . number_format($coupon->min_amount, 2) . ' required to use this coupon.'
            ], 422);
        }

        $discount = 0.00;
        if ($coupon->type === 'percentage') {
            $discount = ($subtotal * $coupon->value) / 100;
        } else {
            $discount = $coupon->value;
        }

        return response()->json([
            'success' => true,
            'message' => 'Coupon code applied successfully!',
            'coupon' => $coupon,
            'discount' => $discount,
        ]);
    }

    /**
     * Get available shipping methods and costs.
     */
    public function shippingMethods(Request $request)
    {
        $request->validate([
            'district_id' => 'required|integer|exists:bd_districts,id',
        ]);

        $cart = $this->getCart($request);
        if ($cart->items->isEmpty()) {
            return response()->json(['success' => false, 'message' => 'Cart is empty.'], 422);
        }

        $district = BdDistrict::find($request->district_id);
        $zone = ($district && strtolower($district->name) === 'dhaka') ? 'inside_dhaka' : 'outside_dhaka';

        // Calculate package weight
        $totalWeight = 0;
        $hasWeightBased = false;
        foreach ($cart->items as $item) {
            if ($item->product->is_weight_based) {
                $hasWeightBased = true;
            }
            if ($item->product->is_weight_based && $item->product->weight_grams) {
                $totalWeight += $item->product->weight_grams * $item->quantity;
            }
        }

        if ($hasWeightBased) {
            $methods = ShippingMethod::where('zone', $zone)
                ->where('is_active', true)
                ->where('pricing_type', 'weight_based')
                ->get();
            
            if ($methods->isNotEmpty()) {
                $formattedMethods = $methods->map(function ($method) use ($totalWeight) {
                    return [
                        'id' => $method->id,
                        'name' => $method->name,
                        'zone' => $method->zone,
                        'cost' => (float)$method->calculateCost($totalWeight),
                        'pricing_type' => $method->pricing_type,
                        'estimated_days' => $method->estimated_days,
                    ];
                });
                return response()->json($formattedMethods);
            }
        }

        $methods = ShippingMethod::where('zone', $zone)
            ->where('is_active', true)
            ->where('pricing_type', 'flat')
            ->get();

        $formattedMethods = $methods->map(function ($method) {
            return [
                'id' => $method->id,
                'name' => $method->name,
                'zone' => $method->zone,
                'cost' => (float)$method->cost,
                'pricing_type' => $method->pricing_type,
                'estimated_days' => $method->estimated_days,
            ];
        });

        return response()->json($formattedMethods);
    }

    /**
     * Place an order from the cart.
     */
    public function placeOrder(Request $request)
    {
        Log::info('CheckoutApiController::placeOrder request payload:', $request->all());

        $request->validate([
            'customer_name' => 'required|string|max:255',
            'customer_phone' => 'required|string|min:11|max:15',
            'customer_email' => 'nullable|email',
            'selected_division_id' => 'required|exists:bd_divisions,id',
            'selected_district_id' => 'required|exists:bd_districts,id',
            'selected_upazila_id' => 'required|exists:bd_upazilas,id',
            'street_address' => 'required|string|max:500',
            'selected_shipping_method_id' => 'required_if:is_buy_now,false|exists:shipping_methods,id', // wait, wait, the original was:
            // Let's copy it exactly:
            'selected_shipping_method_id' => 'required|exists:shipping_methods,id',
            'payment_method' => 'required|in:cod,sslcommerz,bkash,nagad',
            'notes' => 'nullable|string|max:1000',
            'same_as_shipping' => 'required|boolean',
            
            // Billing (Conditional)
            'selected_billing_division_id' => 'required_if:same_as_shipping,false|nullable|exists:bd_divisions,id',
            'selected_billing_district_id' => 'required_if:same_as_shipping,false|nullable|exists:bd_districts,id',
            'selected_billing_upazila_id' => 'required_if:same_as_shipping,false|nullable|exists:bd_upazilas,id',
            'billing_street_address' => 'required_if:same_as_shipping,false|nullable|string|max:500',
            
            // Coupon
            'coupon_code' => 'nullable|string',
        ]);

        // Validate if the selected payment gateway is enabled in database settings
        $paymentMethod = $request->payment_method;
        $isEnabled = match ($paymentMethod) {
            'cod' => \App\Models\Setting::getValue('cod_enabled', true),
            'sslcommerz' => \App\Models\Setting::getValue('sslcommerz_enabled', false),
            'bkash' => \App\Models\Setting::getValue('bkash_enabled', false),
            'nagad' => \App\Models\Setting::getValue('nagad_enabled', false),
            default => false,
        };
        
        $isEnabled = ($isEnabled === true || $isEnabled === 1 || $isEnabled === '1' || $isEnabled === 'true');

        if (!$isEnabled) {
            return response()->json([
                'success' => false,
                'message' => 'The selected payment method (' . strtoupper($paymentMethod) . ') is currently disabled.'
            ], 422);
        }

        $cart = $this->getCart($request);
        if ($cart->items->isEmpty()) {
            return response()->json(['success' => false, 'message' => 'Your cart is empty.'], 422);
        }

        $user = Auth::user();
        $isReseller = $user && $user->role === 'reseller';
        $isResellerOrder = $isReseller && filter_var($request->input('is_reseller_order', true), FILTER_VALIDATE_BOOLEAN);
        $resellerSellingPrices = $request->input('reseller_selling_prices', []);

        // Parse cart items into helper array
        $cartItems = [];
        $subtotal = 0.00;
        $totalResellerPrice = 0.00;
        $totalResellerProfit = 0.00;

        foreach ($cart->items as $item) {
            $resellerPrice = (float) $item->product->reseller_price;

            if ($isReseller) {
                if ($isResellerOrder) {
                    $price = isset($resellerSellingPrices[$item->id]) 
                        ? (float) $resellerSellingPrices[$item->id]
                        : ($item->variant && $item->variant->price !== null && floatval($item->variant->price) > 0 
                            ? (float)$item->variant->price 
                            : (float)$item->product->price);
                    
                    if ($price < $resellerPrice) {
                        $price = $resellerPrice;
                    }
                    $resellerProfit = max(0.00, $price - $resellerPrice) * $item->quantity;
                } else {
                    $price = $resellerPrice;
                    $resellerProfit = 0.00;
                }
            } else {
                $price = $item->variant && $item->variant->price !== null && floatval($item->variant->price) > 0
                    ? $item->variant->price 
                    : $item->product->price;
                $resellerPrice = 0.00;
                $resellerProfit = 0.00;
            }

            $cartItems[] = [
                'id' => $item->id,
                'product_id' => $item->product_id,
                'name' => $item->product->name,
                'sku' => $item->variant?->sku ?? $item->product->sku,
                'price' => (float)$price,
                'reseller_price' => (float)$resellerPrice,
                'reseller_profit' => (float)$resellerProfit,
                'quantity' => $item->quantity,
                'options' => $item->variant?->options ?? [],
            ];
            $subtotal += $price * $item->quantity;
            $totalResellerPrice += $resellerPrice * $item->quantity;
            $totalResellerProfit += $resellerProfit;
        }

        // Start Transaction
        DB::beginTransaction();

        try {
            // 1. Stock validation
            foreach ($cartItems as $item) {
                $product = Product::find($item['product_id']);
                if (isset($item['options']) && !empty($item['options'])) {
                    $variant = ProductVariant::where('product_id', $item['product_id'])
                        ->where('sku', $item['sku'])
                        ->first();
                    if (!$variant || $variant->quantity < $item['quantity']) {
                        throw new Exception('Stock error: Variant "' . $item['name'] . '" does not have enough stock.');
                    }
                } else {
                    if (!$product || $product->quantity < $item['quantity']) {
                        throw new Exception('Stock error: Product "' . $item['name'] . '" does not have enough stock.');
                    }
                }
            }

            // 2. Generate unique order number
            if ($isReseller) {
                $resellerCode = $user->reseller_code ?? \App\Models\User::generateUniqueResellerCode();
                if ($user && !$user->reseller_code) {
                    $user->reseller_code = $resellerCode;
                    $user->saveQuietly();
                }
                $orderNumber = 'RES-' . $resellerCode . '-' . date('ymd') . '-' . rand(1000, 9999);
                while (Order::where('order_number', $orderNumber)->exists()) {
                    $orderNumber = 'RES-' . $resellerCode . '-' . date('ymd') . '-' . rand(1000, 9999);
                }
            } else {
                $orderNumber = 'ORD-' . date('ymd') . rand(10000, 99999);
                while (Order::where('order_number', $orderNumber)->exists()) {
                    $orderNumber = 'ORD-' . date('ymd') . rand(10000, 99999);
                }
            }

            // 3. Addresses formatting
            $div = BdDivision::find($request->selected_division_id)?->name;
            $dist = BdDistrict::find($request->selected_district_id)?->name;
            $up = BdUpazila::find($request->selected_upazila_id)?->name;
            $shippingAddress = "{$request->street_address}, {$up}, {$dist}, {$div}";

            if ($request->same_as_shipping) {
                $billingAddress = $shippingAddress;
            } else {
                $bDiv = BdDivision::find($request->selected_billing_division_id)?->name;
                $bDist = BdDistrict::find($request->selected_billing_district_id)?->name;
                $bUp = BdUpazila::find($request->selected_billing_upazila_id)?->name;
                $billingAddress = "{$request->billing_street_address}, {$bUp}, {$bDist}, {$bDiv}";
            }

            // 4. Compute Dynamic totals
            $vatRate = (float)Setting::getValue('vat_percentage', 0.00);
            $tax = ($subtotal * $vatRate) / 100;

            // Compute Shipping Cost
            $shippingMethod = ShippingMethod::find($request->selected_shipping_method_id);
            $totalWeight = 0;
            foreach ($cart->items as $item) {
                if ($item->product->is_weight_based && $item->product->weight_grams) {
                    $totalWeight += $item->product->weight_grams * $item->quantity;
                }
            }
            $shippingCost = $shippingMethod ? $shippingMethod->calculateCost($totalWeight) : 80.00;

            // Coupon Discount
            $discount = 0.00;
            $appliedCoupon = null;
            if ($request->filled('coupon_code')) {
                $coupon = Coupon::where('code', strtoupper(trim($request->coupon_code)))
                    ->where('is_active', true)
                    ->first();
                if ($coupon && $subtotal >= $coupon->min_amount) {
                    $appliedCoupon = $coupon;
                    $discount = $coupon->type === 'percentage' 
                        ? ($subtotal * $coupon->value) / 100 
                        : $coupon->value;
                }
            }

            $grandTotal = max(($subtotal + $shippingCost + $tax) - $discount, 0.00);

            // 5. Create Order
            $orderData = [
                'order_number' => $orderNumber,
                'user_id' => Auth::check() ? Auth::id() : null,
                'customer_name' => $request->customer_name,
                'customer_phone' => $request->customer_phone,
                'customer_email' => $request->customer_email ?: null,
                'shipping_address' => $shippingAddress,
                'billing_address' => $billingAddress,
                'notes' => $request->notes ?: null,
                
                'shipping_method_id' => $request->selected_shipping_method_id,
                'subtotal' => $subtotal,
                'shipping_cost' => $shippingCost,
                'tax' => $tax,
                'discount' => $discount,
                'grand_total' => $grandTotal,
                
                'payment_method' => $request->payment_method,
                'payment_status' => 'pending',
                'status' => 'pending',
                'coupon_code' => $appliedCoupon?->code ?? null,
            ];

            if ($isReseller) {
                $orderData['is_reseller_order'] = true;
                $orderData['reseller_id'] = $user->id;
                $orderData['total_reseller_price'] = $totalResellerPrice;
                $orderData['total_reseller_profit'] = $totalResellerProfit;
            }

            $order = Order::create($orderData);

            // 6. Deduct stock using backend observer adjustments
            foreach ($cartItems as $item) {
                $product = Product::find($item['product_id']);
                if (isset($item['options']) && !empty($item['options'])) {
                    $variant = ProductVariant::where('product_id', $item['product_id'])
                        ->where('sku', $item['sku'])
                        ->first();

                    \App\Services\StockAdjustmentService::adjust($variant, -$item['quantity'], 'order_placed', [
                        'order_id' => $order->id,
                        'notes' => "Deducted variant stock for Order #{$order->order_number}",
                    ]);
                    \App\Services\StockAdjustmentService::adjust($product, -$item['quantity'], 'order_placed', [
                        'order_id' => $order->id,
                        'notes' => "Deducted product stock aggregate for Order #{$order->order_number}",
                    ]);
                } else {
                    \App\Services\StockAdjustmentService::adjust($product, -$item['quantity'], 'order_placed', [
                        'order_id' => $order->id,
                        'notes' => "Deducted product stock for Order #{$order->order_number}",
                    ]);
                }
            }

            // 7. Create Order Items
            \App\Services\StockAdjustmentService::$skipObserver = true;
            foreach ($cartItems as $item) {
                $variantId = null;
                if (isset($item['options']) && !empty($item['options'])) {
                    $variantId = ProductVariant::where('product_id', $item['product_id'])
                        ->where('sku', $item['sku'])
                        ->value('id');
                }

                $orderItemData = [
                    'order_id' => $order->id,
                    'product_id' => $item['product_id'],
                    'product_variant_id' => $variantId,
                    'product_name' => $item['name'],
                    'product_sku' => $item['sku'],
                    'price' => $item['price'],
                    'quantity' => $item['quantity'],
                ];

                if ($isReseller) {
                    $orderItemData['reseller_price'] = $item['reseller_price'];
                    $orderItemData['reseller_profit'] = $item['reseller_profit'];
                }

                OrderItem::create($orderItemData);
            }
            \App\Services\StockAdjustmentService::$skipObserver = false;

            // 8. Record audit log history
            OrderHistory::create([
                'order_id' => $order->id,
                'user_id' => Auth::check() ? Auth::id() : null,
                'action' => 'order_created',
                'from_status' => null,
                'to_status' => 'pending',
                'notes' => 'Customer placed order via API with ' . strtoupper($request->payment_method) . '.',
            ]);

            DB::commit();

            // Clear Cart
            $cart->items()->delete();

            // 9. Process payment redirection URL
            $redirectUrl = '';
            if ($request->payment_method === 'cod') {
                try {
                    \App\Services\NotificationDispatcher::dispatchOrderPlaced($order);
                } catch (Exception $e) {
                    Log::error('Failed E2E placed notification: ' . $e->getMessage());
                }
                $redirectUrl = env('NEXTJS_FRONTEND_URL', 'http://localhost:3000') . '/checkout/success/' . $order->id;
            } else if ($request->payment_method === 'sslcommerz') {
                $redirectUrl = route('payment.ssl.initiate', ['order' => $order->id]);
            } else if ($request->payment_method === 'bkash') {
                $redirectUrl = route('payment.bkash.initiate', ['order' => $order->id]);
            } else if ($request->payment_method === 'nagad') {
                $redirectUrl = route('payment.nagad.initiate', ['order' => $order->id]);
            }

            return response()->json([
                'success' => true,
                'message' => 'Order created successfully.',
                'order_id' => $order->id,
                'order_number' => $order->order_number,
                'redirect_url' => $redirectUrl,
            ]);

        } catch (Exception $e) {
            DB::rollBack();
            Log::error('API Order placement failed: ' . $e->getMessage());
            return response()->json(['success' => false, 'message' => $e->getMessage()], 422);
        }
    }

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

        $orderNumber = trim($request->order_number ?? '');
        $rawPhone    = trim($request->phone ?? '');

        if (!$orderNumber && !$rawPhone) {
            return response()->json([
                'success' => false,
                'message' => 'Please provide an order number or phone number.',
            ], 422);
        }

        // Normalize phone: keep last 11 digits
        $phone = '';
        if ($rawPhone) {
            $phone = preg_replace('/[^0-9]/', '', $rawPhone);
            if (strlen($phone) > 11) {
                $phone = substr($phone, -11);
            }
        }

        $query = Order::with(['items.product', 'histories', 'courierBooking']);

        if ($orderNumber && $phone) {
            // Both provided — match both for precision
            $query->where('order_number', $orderNumber)
                  ->where(function ($q) use ($phone) {
                      $q->where('customer_phone', 'like', "%{$phone}")
                        ->orWhere('customer_phone', $phone);
                  });
        } elseif ($orderNumber) {
            // Only order number
            $query->where('order_number', $orderNumber);
        } else {
            // Only phone — return the most recent matching order
            $query->where(function ($q) use ($phone) {
                $q->where('customer_phone', 'like', "%{$phone}")
                  ->orWhere('customer_phone', $phone);
            })->orderByDesc('created_at');
        }

        $order = $query->first();

        if (!$order) {
            return response()->json([
                'success' => false,
                'message' => 'No order found with the provided details.',
            ], 404);
        }

        return response()->json([
            'success' => true,
            'data' => [
                'id' => $order->id,
                'order_number' => $order->order_number,
                'customer_name' => $order->customer_name,
                'customer_phone' => $order->customer_phone,
                'delivery_address' => $order->shipping_address,
                'status' => $order->status,
                'payment_status' => $order->payment_status,
                'payment_method' => $order->payment_method,
                'shipping_method' => $order->shipping_method_name ?: ($order->shipping_method_id ? 'Courier' : 'Standard'),
                'subtotal' => $order->subtotal,
                'shipping_cost' => $order->shipping_cost,
                'discount' => $order->discount,
                'grand_total' => $order->grand_total,
                'created_at' => $order->created_at,
                'items' => $order->items->map(function ($item) {
                    return [
                        'product_name' => $item->product_name,
                        'product_sku' => $item->product_sku,
                        'price' => $item->price,
                        'quantity' => $item->quantity,
                        'image' => $item->product?->media?->first()?->original_url ?: null,
                    ];
                }),
                'history' => $order->histories->map(function ($log) {
                    return [
                        'action' => $log->action,
                        'from_status' => $log->from_status,
                        'to_status' => $log->to_status,
                        'notes' => $log->notes,
                        'created_at' => $log->created_at,
                    ];
                }),
                'tracking_code' => $order->courierBooking?->tracking_code ?: null,
            ]
        ]);
    }
}
