<?php

namespace App\Http\Controllers;

use App\Models\Cart;
use App\Models\Product;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;

class CartController extends Controller
{
    /**
     * Display the shopping cart.
     */
    public function index()
    {
        $cartData = $this->buildCartData();

        return view('shop.cart', $cartData);
    }

    /**
     * Build normalized cart data array for cart views.
     *
     * @return array{cartItems: Collection, cartTotal: float, cartCount: int, subtotal: float, servicesTotal: float}
     */
    private function buildCartData(): array
    {
        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);
                    $price = $item->price ?? ($variant['final_price'] ?? $item->product->price);

                    return [
                        'product' => $item->product,
                        'quantity' => $item->quantity,
                        'price' => $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);
                    $price = $item['price'] ?? ($variant['final_price'] ?? $product->price);

                    $cartItems->push([
                        'product' => $product,
                        'quantity' => $item['quantity'],
                        'price' => $price,
                        'services' => $item['services'] ?? [],
                        'variant' => $variant,
                        'variant_index' => $item['variant_index'] ?? 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']);
        });

        $cartTotal = $subtotal + $servicesTotal;
        $cartCount = $cartItems->sum('quantity');

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

    /**
     * Return normalized cart JSON for AJAX responses.
     */
    private function cartJsonResponse()
    {
        $data = $this->buildCartData();

        return response()->json([
            'success' => true,
            'cartCount' => $data['cartCount'],
            'cartTotal' => $data['cartTotal'],
            'subtotal' => $data['subtotal'],
            'servicesTotal' => $data['servicesTotal'],
        ]);
    }

    /**
     * Add a product to the cart
     */
    public function add(Request $request)
    {
        $request->validate([
            'product_id' => 'required|exists:products,id',
            'quantity' => 'required|integer|min:1|max:10',
            'variant_index' => 'nullable|integer|min:0',
            'variant' => 'nullable|array',
            'services' => 'nullable|array',
            'services.*.id' => 'required_with:services|integer|exists:services,id',
            'services.*.resolved_price' => 'required_with:services|numeric|min:0',
        ]);

        $product = Product::findOrFail($request->product_id);

        $variantIndex = $request->filled('variant_index') ? (int) $request->variant_index : null;
        $variantPayload = $request->input('variant');
        $variant = $this->resolveVariantForProduct($product, $variantIndex, is_array($variantPayload) ? $variantPayload : null);

        if ($product->hasVariants() && ! $variant) {
            return response()->json([
                'success' => false,
                'message' => 'Please select a valid variant',
            ], 422);
        }

        if (! $variant) {
            $variant = [
                'index' => null,
                'label' => $product->unit,
                'unit' => $product->unit,
                'base_unit_price' => (float) $product->price,
                'final_price' => (float) $product->price,
                'sale_price' => $product->sale_price !== null ? (float) $product->sale_price : null,
            ];
        }

        $price = $variant['final_price'] ?? $product->price;
        $services = $request->input('services', []);

        if (auth()->check()) {
            $cart = $this->findUserCartItem(auth()->id(), $product->id, $variant['index'] ?? null);

            if ($cart) {
                $cart->quantity = min($cart->quantity + $request->quantity, 10);
                $cart->services = $services;
                $cart->variant = $variant;
                $cart->variant_index = $variant['index'];
                $cart->price = $price;
                $cart->save();
            } else {
                Cart::create([
                    'user_id' => auth()->id(),
                    'product_id' => $product->id,
                    'quantity' => $request->quantity,
                    'price' => $price,
                    'services' => $services,
                    'variant' => $variant,
                    'variant_index' => $variant['index'],
                    'session_id' => Session::getId(),
                ]);
            }
        } else {
            $cart = session()->get('cart', []);
            $productId = $product->id;
            $cartKey = $this->findSessionCartKey($cart, $productId, $variant['index'] ?? null);
            if (! $cartKey) {
                $cartKey = $this->getSessionCartKey($productId, $variant['index'] ?? null);
            }

            if (isset($cart[$cartKey])) {
                $cart[$cartKey]['quantity'] = min($cart[$cartKey]['quantity'] + $request->quantity, 10);
                $cart[$cartKey]['services'] = $services;
                $cart[$cartKey]['variant'] = $variant;
                $cart[$cartKey]['variant_index'] = $variant['index'];
                $cart[$cartKey]['price'] = $price;
            } else {
                $cart[$cartKey] = [
                    'product_id' => $product->id,
                    'quantity' => $request->quantity,
                    'price' => $price,
                    'services' => $services,
                    'variant' => $variant,
                    'variant_index' => $variant['index'],
                ];
            }

            session()->put('cart', $cart);
        }

        return response()->json([
            'success' => true,
            'message' => 'Product added to cart',
            'cartCount' => $this->getCartCount(),
        ]);
    }

    /**
     * Update cart item quantity
     */
    public function update(Request $request)
    {
        $request->validate([
            'product_id' => 'required|exists:products,id',
            'quantity' => 'required|integer|min:1|max:10',
            'variant_index' => 'nullable|integer|min:0',
        ]);

        $variantIndex = $request->filled('variant_index') ? (int) $request->variant_index : null;

        if (auth()->check()) {
            $cart = $this->findUserCartItem(auth()->id(), (int) $request->product_id, $variantIndex);

            if ($cart) {
                $cart->quantity = $request->quantity;
                $cart->save();
            }
        } else {
            $cart = session()->get('cart', []);
            $cartKey = $this->findSessionCartKey($cart, (int) $request->product_id, $variantIndex);
            if (! $cartKey) {
                $cartKey = $this->getSessionCartKey((int) $request->product_id, $variantIndex);
            }
            if (isset($cart[$cartKey])) {
                $cart[$cartKey]['quantity'] = $request->quantity;
                session()->put('cart', $cart);
            }
        }

        return redirect()->route('cart.index');
    }

    /**
     * Get available services for a product (for cart service toggles).
     */
    public function getProductServices(Request $request)
    {
        $request->validate([
            'product_id' => 'required|exists:products,id',
        ]);

        $resolver = new \App\Services\ServicePriceResolver;
        $services = $resolver->resolveServicesForProduct($request->product_id);

        return response()->json([
            'success' => true,
            'services' => $services->map(fn ($item) => [
                'id' => $item['service']->id,
                'name' => $item['service']->name,
                'billing_type' => $item['service']->billing_type,
                'resolved_price' => $item['resolved_price'],
            ]),
        ]);
    }

    /**
     * Remove an item from the cart
     */
    public function remove(Request $request)
    {
        $request->validate([
            'product_id' => 'required|exists:products,id',
            'variant_index' => 'nullable|integer|min:0',
        ]);

        $variantIndex = $request->filled('variant_index') ? (int) $request->variant_index : null;

        if (auth()->check()) {
            $item = $this->findUserCartItem(auth()->id(), (int) $request->product_id, $variantIndex);
            if ($item) {
                $item->delete();
            }
        } else {
            $cart = session()->get('cart', []);
            $cartKey = $this->findSessionCartKey($cart, (int) $request->product_id, $variantIndex)
                ?? $this->getSessionCartKey((int) $request->product_id, $variantIndex);
            unset($cart[$cartKey]);
            session()->put('cart', $cart);
        }

        return redirect()->route('cart.index');
    }

    /**
     * Update services for a cart item.
     */
    public function updateServices(Request $request)
    {
        $request->validate([
            'product_id' => 'required|exists:products,id',
            'variant_index' => 'nullable|integer|min:0',
            'service_ids' => 'nullable|array',
            'service_ids.*' => 'integer|exists:services,id',
        ]);

        $variantIndex = $request->filled('variant_index') ? (int) $request->variant_index : null;

        $resolvedServices = [];

        if ($request->has('service_ids')) {
            $resolver = new \App\Services\ServicePriceResolver;
            $resolved = $resolver->resolveServicesForProduct($request->product_id);

            foreach ($request->service_ids as $serviceId) {
                $serviceData = $resolved->first(function ($item) use ($serviceId) {
                    return $item['service']->id == $serviceId;
                });

                if ($serviceData) {
                    $resolvedServices[] = [
                        'id' => $serviceData['service']->id,
                        'name' => $serviceData['service']->name,
                        'resolved_price' => $serviceData['resolved_price'],
                        'billing_type' => $serviceData['service']->billing_type ?? 'PER_ITEM',
                    ];
                }
            }
        }

        if (auth()->check()) {
            $cart = $this->findUserCartItem(auth()->id(), (int) $request->product_id, $variantIndex);

            if ($cart) {
                $cart->services = $resolvedServices;
                $cart->save();
            }
        } else {
            $cart = session()->get('cart', []);
            $productId = $request->product_id;
            $cartKey = $this->findSessionCartKey($cart, (int) $productId, $variantIndex)
                ?? $this->getSessionCartKey((int) $productId, $variantIndex);

            if (isset($cart[$cartKey])) {
                $cart[$cartKey]['services'] = $resolvedServices;
                session()->put('cart', $cart);
            }
        }

        return redirect()->route('cart.index');
    }

    /**
     * Clear the entire cart
     */
    public function clear()
    {
        if (auth()->check()) {
            // Clear user's cart from database
            Cart::where('user_id', auth()->id())->delete();
        } else {
            // Clear guest cart from session
            session()->forget('cart');
        }

        return redirect()->route('cart.index');
    }

    /**
     * Get cart items count + total for header / sticky bar display.
     */
    public function count()
    {
        return response()->json([
            'count' => $this->getCartCount(),
            'total' => $this->getCartTotal(),
        ]);
    }

    /**
     * Get cart items for current user/session.
     */
    private function getCartItems()
    {
        $sessionId = Session::getId();
        $userId = Auth::id();

        return Cart::with('product.category')
            ->where(function ($query) use ($userId, $sessionId) {
                if ($userId) {
                    $query->where('user_id', $userId);
                } else {
                    $query->where('session_id', $sessionId)->whereNull('user_id');
                }
            })
            ->get();
    }

    /**
     * Verify cart item ownership.
     */
    private function verifyCartOwnership(Cart $cartItem)
    {
        $userId = Auth::id();
        $sessionId = Session::getId();

        if ($userId) {
            return $cartItem->user_id === $userId;
        } else {
            return $cartItem->session_id === $sessionId && is_null($cartItem->user_id);
        }
    }

    /**
     * Transfer guest cart to user cart after login.
     */
    public function transferGuestCart()
    {
        if (! Auth::check()) {
            return;
        }

        $sessionId = Session::getId();
        $userId = Auth::id();

        // Get guest cart items
        $guestCartItems = Cart::where('session_id', $sessionId)
            ->whereNull('user_id')
            ->get();

        foreach ($guestCartItems as $guestItem) {
            $userCartItem = $this->findUserCartItem($userId, $guestItem->product_id, $guestItem->variant_index);

            if ($userCartItem) {
                // Merge quantities
                $userCartItem->update([
                    'quantity' => $userCartItem->quantity + $guestItem->quantity,
                ]);
                $guestItem->delete();
            } else {
                // Transfer to user
                $guestItem->update([
                    'user_id' => $userId,
                    'session_id' => null,
                ]);
            }
        }
    }

    /**
     * Transfer session cart items to database cart after user login
     */
    public function transferSessionCartToDatabase($userId, bool $overwriteExistingCart = false)
    {
        $sessionCart = session()->get('cart', []);

        if (empty($sessionCart)) {
            return;
        }

        if ($overwriteExistingCart) {
            Cart::where('user_id', $userId)->delete();
        }

        foreach ($sessionCart as $item) {
            $product = Product::find($item['product_id']);

            if (! $product) {
                continue; // Skip if product doesn't exist
            }

            $variantIndex = isset($item['variant_index']) ? (int) $item['variant_index'] : null;
            $price = $item['price'] ?? (($item['variant']['final_price'] ?? null) ?? $product->price);

            // Check if item already exists in user's database cart
            $existingCartItem = $overwriteExistingCart ? null : $this->findUserCartItem($userId, $product->id, $variantIndex);

            if ($existingCartItem) {
                // Merge quantities (with max limit of 10)
                $existingCartItem->quantity = min($existingCartItem->quantity + $item['quantity'], 10);
                $existingCartItem->services = $item['services'] ?? [];
                $existingCartItem->variant = $item['variant'] ?? null;
                $existingCartItem->variant_index = $variantIndex;
                $existingCartItem->price = $price;
                $existingCartItem->save();
            } else {
                // Create new cart item
                    Cart::create([
                        'user_id' => $userId,
                        'product_id' => $product->id,
                        'quantity' => $item['quantity'],
                        'price' => $price,
                        'services' => $item['services'] ?? [],
                        'variant' => $item['variant'] ?? null,
                        'variant_index' => $variantIndex,
                        'session_id' => null,
                    ]);
            }
        }

        // Clear session cart after transfer
        session()->forget('cart');
    }

    /**
     * Get the total value of items in the cart (using stored deal prices).
     */
    private function getCartTotal()
    {
        $items = $this->getCartItemsForTotal();

        return $items->sum(function ($item) {
            $productTotal = ($item['price'] ?? 0) * $item['quantity'];
            $servicesTotal = collect($item['services'] ?? [])->sum(fn ($s) => $s['resolved_price'] * $item['quantity']);

            return $productTotal + $servicesTotal;
        });
    }

    /**
     * Get cart items (product, quantity, price, services) for the current user/session.
     */
    private function getCartItemsForTotal()
    {
        if (auth()->check()) {
            return Cart::with('product')
                ->where('user_id', auth()->id())
                ->get()
                ->map(function ($item) {
                    $variant = $item->variant ?? $item->product->resolveVariantByIndex($item->variant_index);

                    return [
                        'price' => $item->price ?? $item->product->price,
                        'quantity' => $item->quantity,
                        'services' => $item->services ?? [],
                        'variant' => $variant,
                        'variant_index' => $item->variant_index,
                    ];
                });
        }

        $sessionCart = session()->get('cart', []);
        $items = collect();

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

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

        return $items;
    }

    /**
     * Get the total count of items in the cart
     */
    private function getCartCount()
    {
        if (auth()->check()) {
            return Cart::where('user_id', auth()->id())->sum('quantity');
        } else {
            $cart = session()->get('cart', []);

            return collect($cart)->sum('quantity');
        }
    }

    /**
     * Resolve the correct variant payload for a request.
     */
    private function resolveVariantForProduct(Product $product, ?int $variantIndex, ?array $variantPayload): ?array
    {
        if ($variantIndex !== null) {
            return $product->resolveVariantByIndex($variantIndex);
        }

        if (! empty($variantPayload)) {
            if (isset($variantPayload['index']) && is_numeric($variantPayload['index'])) {
                return $product->resolveVariantByIndex((int) $variantPayload['index']);
            }

            if (! empty($variantPayload['label'])) {
                return $product->resolveVariantByLabel($variantPayload['label']);
            }
        }

        return $product->defaultVariant();
    }

    /**
     * Build the composite session key for a cart item.
     */
    private function getSessionCartKey(int $productId, ?int $variantIndex): string
    {
        return $productId.'|'.($variantIndex === null ? 'default' : $variantIndex);
    }

    /**
     * Find the current session cart key, including the legacy product-only key.
     */
    private function findSessionCartKey(array $cart, int $productId, ?int $variantIndex): ?string
    {
        $preferredKey = $this->getSessionCartKey($productId, $variantIndex);
        if (array_key_exists($preferredKey, $cart)) {
            return $preferredKey;
        }

        if ($variantIndex === null && array_key_exists((string) $productId, $cart)) {
            return (string) $productId;
        }

        return null;
    }

    /**
     * Find a cart row for a user by product and variant.
     */
    private function findUserCartItem(int $userId, int $productId, ?int $variantIndex): ?Cart
    {
        $query = Cart::where('user_id', $userId)
            ->where('product_id', $productId);

        if ($variantIndex === null) {
            $query->whereNull('variant_index');
        } else {
            $query->where('variant_index', $variantIndex);
        }

        return $query->first();
    }
}
