import json
import logging
from datetime import timedelta
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.db.models import Q, Sum
from django.utils.timezone import now
from django.views.decorators.csrf import csrf_exempt
from .models import Cart, CartItem, ShippingAddress, Order, OrderItem, ShippingMethod
from settings.models import Shop
from inventory.models import Product, Category
from .forms import AddToCartForm, GuestCheckoutForm, ShippingAddressForm, SearchForm, ShippingMethodForm
from django.http import JsonResponse
from django.contrib import messages
from .stk_push import MpesaSTKPush
from django.forms.models import model_to_dict


# ----------------- PRODUCT VIEWS -----------------
def product_list(request):
    query = Q()

    # Filter by category
    category_id = request.GET.get('category')
    if category_id:
        query &= Q(category_id=category_id)

    # Filter by special criteria
    special_filter = request.GET.get('filter')
    if special_filter == 'sale':
        query &= Q(discount_price__isnull=False)
    elif special_filter == 'new':
        one_week_ago = now() - timedelta(weeks=1)
        query &= Q(created_at__gte=one_week_ago)
    elif special_filter == 'best_seller':
        query &= Q(is_active=True)

    # Sorting
    sort_option = request.GET.get('sort')
    if sort_option == 'price_asc':
        order_by = 'selling_price'
    elif sort_option == 'price_desc':
        order_by = '-selling_price'
    elif sort_option == 'name':
        order_by = 'name'
    else:
        order_by = '-created_at'

    products = Product.objects.filter(query).order_by(order_by)
    categories = Category.objects.all()

    return render(request, 'shop/product_list.html', {
        'products': products,
        'categories': categories,
    })


def product_detail(request, product_id):
    product = get_object_or_404(Product, id=product_id)
    recommended_products = Product.objects.filter(category=product.category).exclude(id=product.id)[:3]
    return render(request, 'shop/product_detail.html', {
        'product': product,
        'recommended_products': recommended_products,
    })


# ----------------- CART VIEWS -----------------

def add_to_cart(request):
    """
    Add a product to the cart.
    """
    if request.method == 'POST':
        form = AddToCartForm(request.POST)
        if form.is_valid():
            product = get_object_or_404(Product, id=form.cleaned_data['product_id'])
            quantity = form.cleaned_data['quantity']

            # Handle cart for logged-in user or guest
            if request.user.is_authenticated:
                cart, _ = Cart.objects.get_or_create(user=request.user)
            else:
                session_key = request.session.session_key or request.session.save()
                cart, _ = Cart.objects.get_or_create(session_key=session_key)

            # Add item to the cart
            cart_item, created = CartItem.objects.get_or_create(cart=cart, product=product)
            if not created:
                cart_item.quantity += quantity
            cart_item.save()

            messages.success(request, f"{product.name} added to cart.")
            return redirect('shop:cart_detail')

    return redirect('product_list')


def cart_detail(request):
    """
    Display the cart details for the user or guest.
    Creates a cart if it doesn't already exist.
    """
    # Check if the user is authenticated
    if request.user.is_authenticated:
        # Fetch or create the cart for the authenticated user
        cart, created = Cart.objects.get_or_create(user=request.user)
    else:
        # Ensure the session has a key for unauthenticated users
        if not request.session.session_key:
            request.session.create()
        session_key = request.session.session_key

        # Fetch or create a cart for the session
        cart, created = Cart.objects.get_or_create(session_key=session_key, user=None)

    # Prefetch items for performance optimization
    items = cart.items.select_related('product').all()
    total_price = sum(item.get_total_price() for item in items)

    return render(request, 'shop/cart_detail.html', {
        'items': items,
        'total_price': total_price,
    })


def remove_from_cart(request, cart_item_id):
    """
    Remove an item from the cart.
    """
    cart_item = get_object_or_404(CartItem, id=cart_item_id)

    # Ensure the item belongs to the current user's or guest's cart
    if request.user.is_authenticated:
        if cart_item.cart.user != request.user:
            return redirect('cart_detail')
    else:
        session_key = request.session.session_key
        if cart_item.cart.session_key != session_key:
            return redirect('shop:cart_detail')

    cart_item.delete()
    messages.success(request, "Item removed from cart.")
    return redirect('shop:cart_detail')

def update_cart(request):
    if request.method == "POST":
        item_id = request.POST.get('item_id')
        action = request.POST.get('action')
        cart_item = get_object_or_404(CartItem, id=item_id)

        if action == "increment":
            cart_item.quantity += 1
        elif action == "decrement" and cart_item.quantity > 1:
            cart_item.quantity -= 1
        cart_item.save()

    return redirect('shop:cart_detail')


# ----------------- CHECKOUT VIEWS -----------------

def checkout(request):
    """
    Handle checkout process for both logged-in and guest users.
    """
    if request.user.is_authenticated:
        cart = Cart.objects.filter(user=request.user).first()
    else:
        session_key = request.session.session_key or request.session.save()
        cart = Cart.objects.filter(session_key=session_key).first()

    if not cart or not cart.items.exists():
        messages.error(request, "Your cart is empty.")
        return redirect('shop:cart_detail')

    shipping_form = ShippingAddressForm(request.POST or None)
    shipping_methods = ShippingMethod.objects.filter(is_active=True)
    selected_shipping_method = None
    total_price = cart.get_total_price()

    if request.method == "POST":
        shipping_method_id = request.POST.get('shipping_method')
        if shipping_method_id:
            selected_shipping_method = get_object_or_404(ShippingMethod, id=shipping_method_id)
            total_price += selected_shipping_method.cost

        if shipping_form.is_valid():
            if request.user.is_authenticated:
                shipping_address = shipping_form.save(commit=False)
                shipping_address.user = request.user
                shipping_address.save()
            else:
                shipping_address = shipping_form.save(commit=False)
                shipping_address.guest_email = request.POST.get('email')
                shipping_address.save()

            order = Order.objects.create(
                user=request.user if request.user.is_authenticated else None,
                guest_email=shipping_address.guest_email if not request.user.is_authenticated else None,
                shipping_address=shipping_address,
                shipping_method=selected_shipping_method,
                total_price=total_price
            )
            for item in cart.items.all():
                OrderItem.objects.create(
                    order=order,
                    product=item.product,
                    quantity=item.quantity,
                    price=item.get_unit_price()
                )
            cart.items.all().delete()

            phone_number = request.POST.get('phone_number')
            shop = Shop.objects.first()
            if not shop:
                messages.error(request, "Payment configuration is missing.")
                logging.error("Checkout attempted without a Shop configuration.")
                return redirect('shop:checkout')

            mpesa = MpesaSTKPush(shop)
            try:
                response = mpesa.initiate_stk_push(phone_number, total_price, f"Order #{order.id}")
                if response.get("ResponseCode") == "0":
                    order.mpesa_payment_status = 'Pending'
                    order.save()
                    messages.success(request, "STK Push sent. Please complete the payment on your phone.")
                    return redirect('shop:order_confirmation', order_id=order.id)
                else:
                    messages.error(request, f"Failed to initiate payment: {response.get('errorMessage', 'Unknown error')}")
            except Exception as e:
                logging.error(f"STK Push failed for Order #{order.id}: {e}")
                messages.error(request, "An error occurred while processing your payment. Please try again.")
                order.delete()  # Cleanup incomplete orders
                return redirect('shop:checkout')

    return render(request, 'shop/checkout.html', {
        'cart': cart,
        'shipping_methods': shipping_methods,
        'shipping_form': shipping_form,
        'total_price': total_price,
    })


def order_confirmation(request, order_id):
    """
    Display order confirmation details.
    """
    order = get_object_or_404(Order, id=order_id)
    return render(request, 'shop/order_confirmation.html', {'order': order})


# ----------------- ORDER MANAGEMENT VIEWS -----------------

@login_required
def order_list(request):
    """
    Display the logged-in user's order history.
    """
    orders = Order.objects.filter(user=request.user).order_by('-created_at')
    return render(request, 'shop/order_list.html', {'orders': orders})


@login_required
def order_detail(request, order_id):
    """
    Display details of a specific order.
    """
    order = get_object_or_404(Order, id=order_id, user=request.user)
    return render(request, 'shop/order_detail.html', {'order': order})


# ----------------- SEARCH VIEW -----------------

def search(request):
    """
    Search for products by name or description.
    """
    query = request.GET.get('q', '')
    products = Product.objects.filter(Q(name__icontains=query) | Q(description__icontains=query), is_active=True)

    return render(request, 'shop/search_results.html', {'products': products, 'query': query})

# ----------------- MY ACCOUNT -----------------
@login_required
def my_account(request):
    """
    Display user account information.
    """
    user = request.user
    return render(request, 'shop/my_account.html', {'user': user})


# ----------------- MY ORDERS -----------------
@login_required
def my_orders(request):
    """
    Display the logged-in user's order history.
    """
    orders = Order.objects.filter(user=request.user).order_by('-created_at')
    return render(request, 'shop/my_orders.html', {'orders': orders})


@login_required
def order_detail(request, order_id):
    """
    Display details of a specific order.
    """
    order = get_object_or_404(Order, id=order_id, user=request.user)
    return render(request, 'shop/order_detail.html', {'order': order})


# ----------------- SAVED ITEMS -----------------
@login_required
def saved_items(request):
    """
    Display the logged-in user's saved items.
    """
    saved_items = request.user.profile.saved_items.all()  # Assuming a `saved_items` ManyToManyField in the user profile
    return render(request, 'shop/saved_items.html', {'saved_items': saved_items})

def about_us(request):
    """
    About Us page.
    """
    return render(request, 'shop/about_us.html')


def help_center(request):
    """
    Help Center page.
    """
    return render(request, 'shop/help_center.html')


def terms_and_conditions(request):
    """
    Terms & Conditions page.
    """
    return render(request, 'shop/terms_and_conditions.html')


def privacy_policy(request):
    """
    Privacy Policy page.
    """
    return render(request, 'shop/privacy_policy.html')

def search(request):
    """
    Search for products by name or description.
    """
    query = request.GET.get('q', '')
    products = Product.objects.filter(
        Q(name__icontains=query) | Q(description__icontains=query),
        is_active=True
    ) if query else Product.objects.none()

    return render(request, 'shop/search_results.html', {'products': products, 'query': query})


@csrf_exempt
def mpesa_callback(request):
    """
    Handle M-Pesa STK Push payment callback.
    """
    if request.method == "POST":
        try:
            data = json.loads(request.body)
            result_code = data.get("Body", {}).get("stkCallback", {}).get("ResultCode")
            merchant_request_id = data.get("Body", {}).get("stkCallback", {}).get("MerchantRequestID")

            if result_code == 0:
                # Payment successful
                order = Order.objects.filter(account_reference=merchant_request_id).first()
                if order:
                    order.payment_status = "Paid"
                    order.mpesa_payment_status = "Paid"
                    order.save()
                    logging.info(f"Payment successful for Order #{order.id}")
                return JsonResponse({"status": "success", "message": "Payment successful."})

            # Log failure reason
            logging.warning(f"Payment failed for MerchantRequestID {merchant_request_id}: ResultCode {result_code}")
            return JsonResponse({"status": "failure", "message": "Payment failed or cancelled."})
        except Exception as e:
            logging.error(f"Error processing M-Pesa callback: {e}")
            return JsonResponse({"status": "error", "message": "Callback processing failed."})

    return JsonResponse({"status": "error", "message": "Invalid request method."})



@login_required
def shipping_method_list(request):
    """
    List all shipping methods and display the form for creating a new method.
    """
    shipping_methods = ShippingMethod.objects.all()
    form = ShippingMethodForm()  # Empty form for creating a new shipping method
    return render(request, 'shop/shipping_method_list.html', {
        'shipping_methods': shipping_methods,
        'form': form,
    })


@login_required
def shipping_method_create(request):
    """
    Create a new shipping method and redirect to the list view.
    """
    if request.method == 'POST':
        form = ShippingMethodForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, "Shipping method created successfully.")
        else:
            messages.error(request, "There were errors in the form. Please correct them.")
    return redirect('shop:shipping')


@login_required
def shipping_method_edit(request, pk):
    """
    Edit an existing shipping method.
    """
    shipping_method = get_object_or_404(ShippingMethod, pk=pk)
    if request.method == 'POST':
        form = ShippingMethodForm(request.POST, instance=shipping_method)
        if form.is_valid():
            form.save()
            messages.success(request, "Shipping method updated successfully.")
        else:
            messages.error(request, "There were errors in the form. Please correct them.")
        return redirect('shop:shipping_method_list')
    else:
        messages.error(request, "Invalid request method.")
        return redirect('shop:shipping_method_list')


@login_required
def shipping_method_delete(request, pk):
    """
    Delete an existing shipping method and redirect to the list view.
    """
    shipping_method = get_object_or_404(ShippingMethod, pk=pk)
    if request.method == 'POST':
        shipping_method.delete()
        messages.success(request, "Shipping method deleted successfully.")
    else:
        messages.error(request, "Invalid request method.")
    return redirect('shop:shipping_method_list')