import json
from decimal import Decimal

from django.http import JsonResponse
from django.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST

from .models import Cart, CartItem, Order, PendingCheckout
from vendors.models import VendorProduct


def _compute_delivery_breakdown(items):
    """
    Compute per-vendor delivery and cart totals for a unified cart.
    """
    grouped = {}
    for item in items:
        vendor = item.product.vendor
        bucket = grouped.setdefault(
            vendor.id,
            {
                "vendor_id": str(vendor.id),
                "vendor_name": vendor.store_name,
                "vendor": vendor,
                "items_subtotal": Decimal("0"),
                "delivery_fee": Decimal("0"),
                "vendor_total": Decimal("0"),
            },
        )
        bucket["items_subtotal"] += item.subtotal

    rows = []
    items_subtotal = Decimal("0")
    delivery_total = Decimal("0")
    for _, row in grouped.items():
        items_subtotal += row["items_subtotal"]

        vendor = row.get("vendor")
        fee = Decimal("0")
        if vendor:
            fee = vendor.delivery_fee or Decimal("0")
            threshold = vendor.free_delivery_threshold
            if threshold is not None and row["items_subtotal"] >= threshold:
                fee = Decimal("0")

        row["delivery_fee"] = fee
        row["vendor_total"] = row["items_subtotal"] + fee
        delivery_total += fee
        row.pop("vendor", None)
        rows.append(row)

    grand_total = items_subtotal + delivery_total
    return rows, items_subtotal, delivery_total, grand_total


def _get_or_create_cart(request):
    """Resolve or create a Cart for the current session/user."""
    if not request.session.session_key:
        request.session.create()

    session_key = request.session.session_key or ""

    if request.user.is_authenticated:
        cart = Cart.objects.filter(user=request.user).first()
        if not cart:
            cart = Cart.objects.create(user=request.user, session_key=session_key)
        return cart

    cart = Cart.objects.filter(session_key=session_key, user__isnull=True).first()
    if not cart:
        cart = Cart.objects.create(session_key=session_key)
    return cart


@csrf_exempt
@require_POST
def cart_add(request):
    try:
        body = json.loads(request.body)
    except json.JSONDecodeError:
        return JsonResponse({"error": "Invalid JSON"}, status=400)

    product_id = body.get("product_id")
    quantity = int(body.get("quantity", 1))

    if not product_id:
        return JsonResponse({"error": "Product ID required"}, status=400)

    try:
        product = VendorProduct.objects.get(id=product_id, in_stock=True)
    except VendorProduct.DoesNotExist:
        return JsonResponse({"error": "Product not found or out of stock"}, status=404)

    # Check available stock before adding
    if product.quantity < quantity:
        return JsonResponse({
            "error": f"Only {product.quantity} unit(s) available"
        }, status=400)

    cart = _get_or_create_cart(request)

    item, created = CartItem.objects.get_or_create(
        cart=cart,
        product=product,
        defaults={"quantity": quantity},
    )
    if not created:
        # Check total quantity won't exceed stock
        new_qty = item.quantity + quantity
        if new_qty > product.quantity:
            return JsonResponse({
                "error": f"Only {product.quantity} unit(s) available (you already have {item.quantity} in cart)"
            }, status=400)
        item.quantity = new_qty
        item.save(update_fields=["quantity"])

    return JsonResponse({
        "success": True,
        "cart_count": cart.item_count,
        "message": f"{product.name} added to cart",
    })


@csrf_exempt
@require_POST
def cart_remove(request):
    try:
        body = json.loads(request.body)
    except json.JSONDecodeError:
        return JsonResponse({"error": "Invalid JSON"}, status=400)

    item_id = body.get("item_id")
    cart = _get_or_create_cart(request)

    try:
        item = CartItem.objects.get(id=item_id, cart=cart)
        item.delete()
    except CartItem.DoesNotExist:
        pass

    return JsonResponse({"success": True, "cart_count": cart.item_count})


@csrf_exempt
@require_POST
def cart_update(request):
    try:
        body = json.loads(request.body)
    except json.JSONDecodeError:
        return JsonResponse({"error": "Invalid JSON"}, status=400)

    item_id = body.get("item_id")
    quantity = int(body.get("quantity", 1))
    cart = _get_or_create_cart(request)

    try:
        item = CartItem.objects.get(id=item_id, cart=cart)
        if quantity <= 0:
            item.delete()
        else:
            # Don't allow setting qty above available stock
            if quantity > item.product.quantity:
                quantity = item.product.quantity
            if quantity <= 0:
                item.delete()
            else:
                item.quantity = quantity
                item.save(update_fields=["quantity"])
    except CartItem.DoesNotExist:
        pass

    return JsonResponse({"success": True, "cart_count": cart.item_count})


def cart_view(request):
    cart = _get_or_create_cart(request)
    items = cart.items.select_related("product", "product__vendor").prefetch_related("product__images").all()
    vendor_breakdown, items_subtotal, delivery_total, grand_total = _compute_delivery_breakdown(items)

    if request.headers.get("X-Requested-With") == "XMLHttpRequest":
        items_data = []
        for item in items:
            items_data.append({
                "id": item.id,
                "product_id": item.product.id,
                "name": item.product.name,
                "price": float(item.product.price),
                "currency": item.product.currency,
                "image_url": item.product.primary_image_url,
                "store_name": item.product.vendor.store_name,
                "quantity": item.quantity,
                "subtotal": float(item.subtotal),
                "in_stock": item.product.in_stock,
                "available_qty": item.product.quantity,
            })
        return JsonResponse({
            "items": items_data,
            "items_subtotal": float(items_subtotal),
            "delivery_total": float(delivery_total),
            "total": float(grand_total),
            "count": cart.item_count,
            "vendor_breakdown": [
                {
                    "vendor_id": row["vendor_id"],
                    "vendor_name": row["vendor_name"],
                    "items_subtotal": float(row["items_subtotal"]),
                    "delivery_fee": float(row["delivery_fee"]),
                    "vendor_total": float(row["vendor_total"]),
                }
                for row in vendor_breakdown
            ],
        })

    return render(request, "cart/cart.html", {
        "cart": cart,
        "items": items,
        "vendor_breakdown": vendor_breakdown,
        "items_subtotal": items_subtotal,
        "delivery_total": delivery_total,
        "grand_total": grand_total,
    })


def cart_count(request):
    cart = _get_or_create_cart(request)
    return JsonResponse({"count": cart.item_count})


def checkout(request):
    """
    Pay-first checkout: buyer pays via Paystack before any Order or VendorOrder exists.
    Order, vendor splits, escrows, and vendor notifications are created when payment verifies.
    """
    
    cart = _get_or_create_cart(request)

    # WhatsApp session bridge: if wa_session param present, merge WA cart into this cart
    wa_session_key = request.GET.get("wa_session", "").strip()
    if wa_session_key:
        from django.core.cache import cache
        wa_items = cache.get(f"wa_session:{wa_session_key.replace('wa:', '')}:cart", [])
        for item in wa_items:
            if item.get("vendor_id"):
                try:
                    from vendors.models import VendorProduct
                    product = VendorProduct.objects.get(id=item["product_id"], in_stock=True)
                    CartItem.objects.update_or_create(
                        cart=cart, product=product,
                        defaults={"quantity": item["quantity"]},
                    )
                except VendorProduct.DoesNotExist:
                    pass
        if wa_items:
            from whatsapp.session import clear_cart as wa_clear_cart
            wa_clear_cart(wa_session_key.replace("wa:", ""))
            
    items = cart.items.select_related("product", "product__vendor").all()
    vendor_breakdown, items_subtotal, delivery_total, grand_total = _compute_delivery_breakdown(items)

    if not items.exists():
        return redirect("cart:view")

    if request.method == "POST":
        customer_name = request.POST.get("customer_name", "").strip()
        customer_email = request.POST.get("customer_email", "").strip()
        customer_phone = request.POST.get("customer_phone", "").strip()
        delivery_address = request.POST.get("delivery_address", "").strip()

        errors = []
        if not customer_name:
            errors.append("Name is required.")
        if not customer_email:
            errors.append("Email is required.")
        if not delivery_address:
            errors.append("Delivery address is required.")
        if not customer_phone:
            errors.append("Phone number is required — we send delivery confirmation via SMS.")

        # Check stock availability for all items
        stock_errors = []
        for item in items:
            if not item.product.in_stock:
                stock_errors.append(
                    f"{item.product.name} is no longer in stock and cannot be purchased."
                )
            elif item.quantity > item.product.quantity:
                stock_errors.append(
                    f"{item.product.name}: only {item.product.quantity} unit(s) available "
                    f"but you have {item.quantity} in your cart."
                )
        errors.extend(stock_errors)

        if errors:
            return render(request, "cart/checkout.html", {
                "cart": cart,
                "items": items,
                "vendor_breakdown": vendor_breakdown,
                "items_subtotal": items_subtotal,
                "delivery_total": delivery_total,
                "grand_total": grand_total,
                "errors": errors,
                "form_data": request.POST,
            })

        lines = []
        for item in items:
            lines.append({
                "product_id": str(item.product_id),
                "quantity": item.quantity,
                "product_price": str(item.product.price),
                "product_name": item.product.name,
                "currency": item.product.currency,
                "vendor_id": str(item.product.vendor_id),
                "line_subtotal": str(item.subtotal),
            })

        pending = PendingCheckout.objects.create(
            cart=cart,
            user=request.user if request.user.is_authenticated else None,
            session_key=request.session.session_key or "",
            customer_name=customer_name,
            customer_email=customer_email,
            customer_phone=customer_phone,
            delivery_address=delivery_address,
            items_subtotal=items_subtotal,
            delivery_total=delivery_total,
            total_amount=grand_total,
            lines=lines,
            vendor_breakdown=[
                {
                    "vendor_id": row["vendor_id"],
                    "items_subtotal": str(row["items_subtotal"]),
                    "delivery_fee": str(row["delivery_fee"]),
                    "vendor_total": str(row["vendor_total"]),
                }
                for row in vendor_breakdown
            ],
        )

        try:
            from escrow.services.escrow_service import initiate_escrow_payment_pending
            ps_data = initiate_escrow_payment_pending(pending, request)
            return redirect(ps_data["authorization_url"])
        except Exception as exc:
            import logging
            logging.getLogger(__name__).error("Escrow payment init failed: %s", exc)
            pending.delete()
            errors = [
                "We could not start payment. Please try again in a moment or contact support.",
            ]
            return render(request, "cart/checkout.html", {
                "cart": cart,
                "items": items,
                "vendor_breakdown": vendor_breakdown,
                "items_subtotal": items_subtotal,
                "delivery_total": delivery_total,
                "grand_total": grand_total,
                "errors": errors,
                "form_data": request.POST,
            })

    # GET: check for any out-of-stock items and warn the user
    stock_warnings = []
    for item in items:
        if not item.product.in_stock:
            stock_warnings.append(f"{item.product.name} is out of stock.")
        elif item.quantity > item.product.quantity:
            stock_warnings.append(
                f"{item.product.name}: only {item.product.quantity} unit(s) available."
            )

    return render(request, "cart/checkout.html", {
        "cart": cart,
        "items": items,
        "vendor_breakdown": vendor_breakdown,
        "items_subtotal": items_subtotal,
        "delivery_total": delivery_total,
        "grand_total": grand_total,
        "stock_warnings": stock_warnings,
    })


def order_confirmation(request, order_id):
    """Fallback confirmation page (shown if Paystack unavailable)."""
    order = get_object_or_404(Order, id=order_id)
    items = order.items.select_related("vendor", "product").all()
    vendor_orders = order.vendor_orders.select_related("vendor").all()

    return render(request, "cart/order_confirmation.html", {
        "order": order,
        "items": items,
        "vendor_orders": vendor_orders,
    })
