import uuid
import requests
import logging
logger = logging.getLogger(__name__)

from decimal import Decimal
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.shortcuts import get_object_or_404
from shops.models import ProductionUnit, Shop
from products.models import CustomProduct, ProductImage, Products, Wishlist,SalesUnitProductSelection
from .models import (
    Cart,
    CartItem,
    SKU,
    DeliverySlot,
    Discount,
    Orders,
    Dropaddress,
    OrderProducts,
    Coupons,
    Payment,
    Ads,
OrderDelivery,
NotificationStat
)
from accounts.models import UserLocation, Address
from datetime import datetime, timedelta
from django.utils import timezone
from django.db import transaction
from .serializers import OrderSerializer, PastOrderListSerializer, AdSerializer,OrderSerializer2, CourierDetailsSerializer, CustomOrderSerializer,OrderBillDetailsSerializer
from products.serializers import CustomProductSerializer
import random, string, googlemaps
import razorpay
from django.conf import settings
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.db.models import Q



class CartViewMixin:
    def get_cart_items_response(self, cart, message):
        """Helper method to format cart items into a response."""
        cart_items = cart.items.all()
        items_data = []
        subtotal_sum = 0
        gst_charges_sum = 0
        delivery_fee = 20
        total_savings = 0

        for item in cart_items:
            sku = item.sku
            product = sku.product
            product_image = (
                product.images.first().image.url if product.images.exists() else None
            )

            # Calculate applicable discount
            applicable_discount = None
            discounts = Discount.objects.filter(
                Q(DiscountOn="Category", ApplicableCategory__in=[product.item_category]) |
                Q(DiscountOn="SubCategory", ApplicableSubCategory__in=[product.item_sub_category]) |
                Q(DiscountOn="Product", ApplicableProduct__in=[product]) |
                Q(DiscountOn="Sku", ApplicableSku__in=[sku])
            )

            for discount in discounts:
                if discount.DiscountOn == 'Category' and product.item_category in discount.ApplicableCategory.all():
                    applicable_discount = discount
                    break
                elif discount.DiscountOn == 'SubCategory' and product.item_sub_category in discount.ApplicableSubCategory.all():
                    applicable_discount = discount
                    break
                elif discount.DiscountOn == 'Product' and product in discount.ApplicableProduct.all():
                    applicable_discount = discount
                    break
                elif discount.DiscountOn == 'Sku' and sku in discount.ApplicableSku.all():
                    applicable_discount = discount
                    break

            # Price Calculation (offer price is based on discount)
            sku_mrp = sku.sku_mrp
            if applicable_discount:
                discount_percentage = applicable_discount.DiscountPercentage
                offer_price = round(float(sku_mrp) * (1 - float(discount_percentage) / 100), 2)  # type: ignore
                offer = f"{discount_percentage}%"
            else:
                offer_price = sku_mrp
                offer = "0%"

            # GST Calculation (GST on the original price, not on the offer price)
            gst_values = {
                "i_gst": product.i_gst or 0,
                "s_gst": product.s_gst or 0,
                "c_gst": product.c_gst or 0,
                "cess": product.cess or 0,
            }
            gst_amount = 0
            for gst_type, gst_percent in gst_values.items():
                gst_amount += round(float(sku_mrp) * (float(gst_percent) / 100), 2)

            # Calculate Item Data
            item_data = {
                "cart_item_id": item.id,
                "sku_id": sku.id,
                "sku_name": sku.sku_name,
                "sku_code": sku.sku_code,
                "quantity": item.quantity,
                "sku_price": sku_mrp,
                "offer_price": offer_price,
                "sku_unit": sku.sku_unit,
                "sku_quantity": sku.sku_quantity,
                "product_name": product.item_name,
                "product_image": product_image,
                "offer": offer,
            }

            items_data.append(item_data)

            # Subtotal is based on the original price (before discount)
            subtotal_sum += round(float(sku_mrp) * item.quantity, 2)

            # GST is added on the original price
            gst_charges_sum += float(gst_amount) * item.quantity

            # Calculate total savings (difference between original price and offer price)
            total_savings += round((float(sku_mrp) - float(offer_price)) * item.quantity, 2)

        # Final Price Calculations
        subtotal = round(subtotal_sum, 2)
        taxes_and_charges = round(gst_charges_sum, 2)
        savings = round(total_savings, 2)

        # Apply coupon savings if coupon is applied
        coupon_savings = 0
        if cart.coupon:
            coupon_savings = self.calculate_coupon_savings(cart.coupon, subtotal)

        grand_total = round(float(subtotal) + float(taxes_and_charges) - float(savings) - float(coupon_savings) + float(delivery_fee), 2)

        price_data = {
            "subtotal": subtotal,
            "taxes_and_charges": taxes_and_charges,
            "delivery_fee": round(delivery_fee, 2),
            "discount":savings,
            "total_savings": savings + coupon_savings,
            "coupon_savings": coupon_savings,
            "grand_total": grand_total,
            "coupon_code":cart.coupon.CouponCode if cart.coupon else None
        }

        return Response(
            {
                "status": 1,
                "message": message,
                "data": items_data,
                "price_data": price_data,
            },
            status=status.HTTP_200_OK,
        )

    def calculate_coupon_savings(self, coupon, subtotal):
        """Calculate the savings from the applied coupon."""
        savings = 0
        if coupon.CouponType == "percentage":
            savings = (coupon.DiscountPercentage / 100) * subtotal
            if coupon.MaxDiscountAmountForPercentage:
                savings = min(savings, coupon.MaxDiscountAmountForPercentage)
        elif coupon.CouponType == "amount":
            savings = coupon.DiscountAmount
        return round(savings, 2)


class AddOrUpdateCartView(CartViewMixin, APIView):
    def post(self, request):
        sku_data = request.data.get("skus")
        coupon_code = request.data.get("coupon_code")  # Get the coupon code from the request
        remove_coupon = request.data.get("remove_coupon", False)
        if not sku_data and not coupon_code and not remove_coupon:
            return Response(
                {"status": 0, "message": "SKU data or coupon code must be provided."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        if request.user.is_authenticated:
            user = request.user
            anonymous_id = None
        else:
            anonymous_id = request.data.get("anonymous_id")
            user = None  # No authenticated user, we're using anonymous

        # Get or create the cart for the user or anonymous ID
        cart, created = Cart.objects.get_or_create(
            user=user if user else None, anonymous_id=anonymous_id if not user else None
        )

        # If a coupon is provided, try to find and apply it
        if coupon_code:
            try:
                coupon = Coupons.objects.get(CouponCode=coupon_code, validity_end_date__gte=timezone.now())
                cart.coupon= coupon   # type: ignore
                cart.save()
                return self.get_cart_items_response(cart, "Coupon added successfully!")
            except Coupons.DoesNotExist:
                return Response(
                    {"status": 0, "message": "Invalid or expired coupon code."},
                    status=status.HTTP_400_BAD_REQUEST,
                )
        if remove_coupon:
            cart.coupon = None
            cart.save()
            return self.get_cart_items_response(cart, "Coupon removed successfully!")
        

        # Add or update the cart items
        for sku_info in sku_data:
            sku_id = sku_info.get("sku")
            quantity = sku_info.get("quantity", 1)

            if not sku_id or quantity <= 0:
                return Response(
                    {
                        "status": 0,
                        "message": "SKU ID and quantity must be provided and greater than zero.",
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

            sku = get_object_or_404(SKU, id=sku_id)

            # Handle cart item (either create or update)
            cart_item, created = CartItem.objects.get_or_create(cart=cart, sku=sku)
            cart_item.quantity = quantity
            cart_item.save()

        # Now, return the updated cart with the coupon details if applied
        return self.get_cart_items_response(cart, "Cart Updated Successfully!")



class RemoveFromCartView(CartViewMixin, APIView):
    def delete(self, request, cart_item_id):
        cart_item = get_object_or_404(CartItem, id=cart_item_id)
        cart = cart_item.cart

        # Delete the cart item
        cart_item.delete()

        if not cart.items.exists():  # type: ignore
            # Delete the cart if it's empty
            cart.delete()
            return Response(
                {
                    "status": 1,
                    "message": "Cart is now empty.",
                    "data": None,
                },
                status=status.HTTP_200_OK,
            )

        return self.get_cart_items_response(
            cart, "Item removed from cart successfully!"
        )
    
class FlushCartView(CartViewMixin, APIView):
    def post(self, request):
        if request.user.is_authenticated:
            cart = Cart.objects.filter(user=request.user).first()
        else:
            try:
                anonymous_id = request.data.get("anonymous_id")
                cart = Cart.objects.filter(anonymous_id=anonymous_id).first()
            except:
                cart = None
        
        if not cart:
            return Response(
                {"status": 1, "message": "No cart found for this user.", "data": None},
                status=status.HTTP_200_OK,
            )

        cart.items.all().delete()   # type: ignore

        if not cart.items.exists():  # type: ignore
            cart.delete()

        return Response(
            {"status": 1, "message": "Cart flushed successfully.", "data": None},
            status=status.HTTP_200_OK,
        )


class GetCartView(CartViewMixin, APIView):
    def post(self, request):
        if request.user.is_authenticated:
            cart = Cart.objects.filter(user=request.user).first()
        else:
            try:
                anonymous_id = request.data.get("anonymous_id")
                cart = Cart.objects.filter(anonymous_id=anonymous_id).first()
            except:
                cart = None
        if not cart:
            return Response(
                {"status": 1, "message": "No cart found for this user.", "data": None},
                status=status.HTTP_200_OK,
            )

        return self.get_cart_items_response(cart, "Cart retrieved successfully!")


class DeliverySlotSelectionView(APIView):
    def get(self, request):
        try:
            if request.user.is_authenticated:
                user_location = UserLocation.objects.filter(user=request.user).first()
                pickup_available = False
                shop_details = None
                if user_location and user_location.shop:
                    pickup_available = True
                    shop = user_location.shop
                    shop_details = {
                        "shop_name": shop.unit_name,
                        "shop_phone": shop.contact_no,
                        "shop_location": shop.unit_location,
                        "latitude":shop.latitude,
                        "longitude":shop.longitude
                    }
                    
                available_slots = self.get_available_delivery_slots()
                slots_data = []
                current_time = timezone.localtime(timezone.now())


                for slot in available_slots:
                    for day_offset in range(0, 5):
                        future_date = current_time + timedelta(days=day_offset)
                        start_time = timezone.make_aware(
                            datetime.combine(future_date.date(), slot.start_time)
                        )
                        end_time = timezone.make_aware(
                            datetime.combine(future_date.date(), slot.end_time)
                        )

                        if day_offset == 0 and start_time <= current_time:
                            continue

                        delivery_slot = f"{start_time.strftime('%d %b %Y %I:%M %p')} to {end_time.strftime('%I:%M %p')}"

                        slots_data.append(
                            {
                                "delivery_slot": delivery_slot,
                                "slot_id": slot.id, # type: ignore
                                "date": future_date.strftime("%Y-%m-%d"),
                                "start_time": start_time,  
                            }
                        )

                slots_data.sort(key=lambda x: (x["date"], x["start_time"]))

                slots_data = slots_data[:5]

                return Response(
                    {
                        "status": 1,
                        "message": "Available delivery slots retrieved successfully!",
                        "data": slots_data,
                        "pickup_available": pickup_available,
                        "shop_details":shop_details
                    },
                    status=status.HTTP_200_OK,
                )

            else:
                return Response(
                    {
                        "status": 0,
                        "message": "User is not authenticated.",
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

        except Exception as e:
            return Response(
                {
                    "status": 0,
                    "message": "An unexpected error occurred.",
                    "exception": str(e),
                },
                status=status.HTTP_400_BAD_REQUEST,
            )

    def get_available_delivery_slots(self):
        """Retrieve active and available delivery slots."""
        now = timezone.now()

        available_slots = DeliverySlot.objects.filter(
            is_available=True,
            status="active",
        ).order_by(
            "start_time"
        )  # Ensure slots are sorted by their start time

        return available_slots


class CheckoutAPIView(APIView):

    def generate_unique_order_id(self):
        """Generate a unique order ID."""
        while True:
            order_id = f"ID{random.randint(100, 999)}{random.choice(string.ascii_uppercase)}{random.choice(string.ascii_uppercase)}"
            # Ensure order ID is unique by checking if it already exists in the database
            if not Orders.objects.filter(order_ID=order_id).exists():
                return order_id

    def post(self, request, *args, **kwargs):
        try:
            user = request.user

            # Get the cart associated with the user
            cart = get_object_or_404(Cart, user=user)
            pickup = request.data.get("pickup", False)
            address = None
            delivery_slot = None

            # If not a pickup, fetch address and delivery slot details
            if not pickup:
                address_id = request.data.get("address_id")
                address = get_object_or_404(Address, id=address_id)
                """
                # Use Google Maps API to get the full address details (street, city, state, pincode)
                google_maps_api_key = googlemaps.Client(key="AIzaSyAEWPwKw_tv81JSa0x_glii-pz51JCX_PU")

                geocode_url = f"https://maps.googleapis.com/maps/api/geocode/json?latlng={address.latitude},{address.longitude}&key={google_maps_api_key}"
                response = requests.get(geocode_url)
                if response.status_code == 200:
                    data = response.json()
                    if data['status'] == 'OK':
                        # Extract relevant address components from the response
                        address_components = data['results'][0]['address_components']
                        street = city = state = pin_code = None

                        for component in address_components:
                            if "route" in component['types']:
                                street = component['long_name']
                            if "locality" in component['types']:
                                city = component['long_name']
                            if "administrative_area_level_1" in component['types']:
                                state = component['long_name']
                            if "postal_code" in component['types']:
                                pin_code = component['long_name']

                        # Proceed to populate the Dropaddress model with the fetched details
                    else:
                        return Response({"status": 0, "message": "Unable to fetch address details from Google Maps.","exc":str(response.json())}, status=status.HTTP_400_BAD_REQUEST)
                else:
                    return Response({"status": 0, "message": "Error connecting to Google Maps API."}, status=status.HTTP_400_BAD_REQUEST)
"""

                delivery_slot_id = request.data.get("delivery_slot")
                delivery_slot = get_object_or_404(DeliverySlot, id=delivery_slot_id)
                delivery_slot_time = delivery_slot.start_time
            delivery_slot_date = request.data.get("deliver_date")

            # Order type will always be "Pick Up" or "Local Orders" as no long-distance orders are allowed
            user_location = get_object_or_404(UserLocation, user=user)
            shop_uuid = user_location.shop
            order_type = "Pick Up" if pickup else "Local Orders"
            if not user_location.shop:
                order_type = "Long Distance Orders"



            # If delivery is requested, create a Dropaddress
            drop_address = None
            if not pickup:
                drop_address = Dropaddress.objects.create(
                    name=f"{user.first_name} {user.last_name}",
                    house_number_or_name=address.flat_no,  # type: ignore
                    land_mark=address.landmark,  # type: ignore
                    street="street",
                    city="city",
                    state_or_province="state",
                    pin_code=000000,
                    latitude=address.latitude,  # type: ignore
                    longitude=address.longitude,  # type: ignore
                    address_type=address.address_type,  # type: ignore
                    contact_number=user.phone_number,
                )

            # Generate a unique order ID
            order_id = self.generate_unique_order_id()
            cart_mixin = CartViewMixin()
            cart_response = cart_mixin.get_cart_items_response(cart, "Cart data fetched successfully")

            price_data = cart_response.data["price_data"] # type: ignore
            subtotal = price_data["subtotal"]
            taxes_and_charges = price_data["taxes_and_charges"]
            discount = price_data["discount"]
            total_savings = price_data["total_savings"]
            coupon_savings = price_data["coupon_savings"]
            grand_total = price_data["grand_total"]
            delivery_fee = price_data["delivery_fee"]

            with transaction.atomic():
                try:
                    order = Orders.objects.create(
                        order_ID=order_id,
                        order_type=order_type,
                        order_status="New Order",
                        user_uuid=user,
                        drop_address=drop_address if drop_address else None,
                        delivery_instruction=request.data.get("delivery_instruction", ""),
                        cooking_instruction=request.data.get("cooking_instruction", ""),
                        delivery_slot_date=delivery_slot_date,
                        delivery_slot_time=delivery_slot_time if not pickup else None,
                        grand_total=grand_total,
                        taxes_and_charges=taxes_and_charges,
                        delivery_charges=delivery_fee,
                        sub_total=subtotal,
                        total_savings=total_savings,
                        coupon_savings=coupon_savings,
                        discount=discount,
                        color_status = "White",
                        color_status_updation_time=datetime.now()
                    )
                    if order.order_type == "Local Orders" or order.order_type == "Pick Up":
                        order.store_uuid=shop_uuid
                        order.save()

                    elif order.order_type == "Long Distance Orders":
                        try:
                            pu = ProductionUnit.objects.first()
                        except Exception as e:
                            pu = None
                        order.pu_uuid = pu
                        order.save()

                    for cart_item in cart.items.all():  # type: ignore
                        sku = cart_item.sku
                        quantity = cart_item.quantity
                        price = sku.sku_mrp * quantity

                        OrderProducts.objects.create(
                            product_name=sku.product.item_name,
                            order=order,
                            sku=sku,
                            quantity=quantity,
                            price=price,
                        )
                    razorpay_client = razorpay.Client(
                        auth=(settings.RAZORPAY_KEY_ID, settings.RAZORPAY_KEY_SECRET)
                    )
                    razorpay_order = razorpay_client.order.create(  # type: ignore
                        {
                            "amount": int(grand_total * 100),  # amount in paise
                            "currency": "INR",
                            "payment_capture": 1,  # Automatic payment capture
                        }
                    )
                    Payment.objects.create(
                        order=order,
                        razorpay_order_id=razorpay_order["id"],
                        payment_status="pending",   # payment status pending
                    )

                    return Response(
                        {
                            "status": 1,
                            "message": "Order Created Successfully",
                            "data": {
                                "order_id": order.order_ID,
                                "razorpay_order_id": razorpay_order["id"],
                                "razorpay_payment_link": razorpay_order[
                                    "receipt"
                                ],  # URL for the payment page
                                "amount": grand_total,
                            },
                        },
                        status=status.HTTP_201_CREATED,
                    )
                except Exception as e:
                    order.order_status ="Failed"
                    order.save()
                    return Response({
                        "status": 0,
                        "message": "An unexpected error occurred.",
                        "exception": str(e),

                    },
                        status=status.HTTP_400_BAD_REQUEST,
                    )

        except Exception as e:
            return Response(
                {
                    "status": 0,
                    "message": "An unexpected error occurred.",
                    "exception": str(e),
                },
                status=status.HTTP_400_BAD_REQUEST,
            )


class CouponsListView(APIView, CartViewMixin):
    """
    View to list all available valid coupons and calculate the savings based on the user's cart.
    """

    def post(self, request):
        try:
            if request.user.is_authenticated:
                cart = Cart.objects.filter(user=request.user).first()
            else:
                try:
                    anonymous_id = request.data.get("anonymous_id")
                    cart = Cart.objects.filter(anonymous_id=anonymous_id).first()
                except:
                    cart = None

            if not cart:
                return Response(
                    {"status": 0, "message": "No cart found for the user."},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Use the CartViewMixin's method to get the cart's price data (including total)
            price_data_response = self.get_cart_items_response(cart, "Cart details")
            
            # Extract the total amount from the price_data
            cart_total = price_data_response.data.get("price_data", {}).get("subtotal", 0) # type: ignore

            # Get the cart items
            cart_items = CartItem.objects.filter(cart=cart)

            # Only fetch valid coupons
            valid_coupons = Coupons.objects.filter(
                validity_end_date__gte=timezone.now()
            )


            applicable_coupons = []

            # Loop through each valid coupon and check if it's applicable to the cart
            for coupon in valid_coupons:

                applicable_items = self.get_applicable_items(coupon, cart_items)
                savings = 0
                is_applicable = False

                if applicable_items:
                    savings = self.calculate_savings(coupon, applicable_items, cart_total)
                    is_applicable = True
                applicable_coupons.append(
                    {
                        "coupon_name": coupon.CouponName,
                        "coupon_code": coupon.CouponCode,
                        "coupon_description": coupon.CouponDescription,
                        "coupon_type": coupon.CouponType,
                        "minimum_bill_amount": coupon.TotalBillAmount,
                        "price": coupon.DiscountAmount if coupon.CouponType == 'amount' else None,
                        "percentage": coupon.DiscountPercentage if coupon.CouponType == 'percentage' else None,
                        "max_discount_amount": coupon.MaxDiscountAmountForPercentage,
                        "icon": coupon.Icon.url if coupon.Icon else None,
                        "terms_and_conditions": coupon.TermsAndConditions,
                        "savings": savings,
                        "applicable": is_applicable,
                    }
                )

            return Response(
                {
                    "status": 1,
                    "message": "Valid coupons retrieved successfully.",
                    "coupons": applicable_coupons,
                },
                status=status.HTTP_200_OK,
            )
        except Exception as e:
            return Response(
                {
                    "status": 0,
                    "message": "An unexpected error occurred.",
                    "Exception": str(e),
                },
                status=status.HTTP_400_BAD_REQUEST,
            )

    def get_applicable_items(self, coupon, cart_items):
        """
        Check if any items in the cart match the applicable categories, subcategories, or SKUs of the coupon.
        """
        applicable_items = []

        # Check if the coupon applies to specific SKUs in the cart
        if coupon.CouponOn == "Sku":
            applicable_items = [
                item for item in cart_items if item.sku in coupon.ApplicableSku.all()
            ]

        # Check if the coupon applies to specific categories or subcategories
        elif coupon.CouponOn == "Category":
            applicable_items = [
                item
                for item in cart_items
                if item.sku.product.item_category in coupon.ApplicableCategory.all()
            ]
        elif coupon.CouponOn == "SubCategory":
            applicable_items = [
                item
                for item in cart_items
                if item.sku.product.item_sub_category
                in coupon.ApplicableSubCategory.all()
            ]
        elif coupon.CouponOn == "Product":
            applicable_items = [
                item
                for item in cart_items
                if item.sku.product in coupon.ApplicableProduct.all()
            ]

        return applicable_items

    def calculate_savings(self, coupon, applicable_items, cart_total):
        """
        Calculate the savings for the user based on the coupon type (DiscountAmount or DiscountPercentage).
        """
        savings = 0

        # Check if the cart total exceeds the MinimumBillAmount
        if cart_total >= coupon.TotalBillAmount:
            if coupon.CouponType == "amount":
                savings = coupon.DiscountAmount
            elif coupon.CouponType == "percentage":
                savings = (coupon.DiscountPercentage / 100) * cart_total

            # Apply the maximum discount amount cap
                if coupon.MaxDiscountAmountForPercentage:
                    savings = min(savings, coupon.MaxDiscountAmountForPercentage)

        savings = round(savings, 2)

        return savings



class VerifyPaymentAPIView(APIView):
    def post(self, request, *args, **kwargs):

        user = request.user
        razorpay_order_id = request.data.get("razorpay_order_id")
        razorpay_payment_id = request.data.get("razorpay_payment_id")
        razorpay_signature = request.data.get("razorpay_signature")
        try:

            payment = Payment.objects.get(razorpay_order_id=razorpay_order_id)
            order = payment.order

            # Verify the signature using Razorpay's API
            razorpay_client = razorpay.Client(
                auth=(settings.RAZORPAY_KEY_ID, settings.RAZORPAY_KEY_SECRET)
            )
            params = {
                "razorpay_order_id": razorpay_order_id,
                "razorpay_payment_id": razorpay_payment_id,
                "razorpay_signature": razorpay_signature,
            }

            try:
                razorpay_client.utility.verify_payment_signature(params)  # type: ignore

                payment.payment_status = "Paid"
                payment.razorpay_payment_id = razorpay_payment_id
                payment.save()
                order.order_status = "Confirmed"
                order.save()
                cart = get_object_or_404(Cart, user=user)
                cart.delete()


                if order.order_type == "Local Orders":
                    return Response(
                        {
                            "status": 1,
                            "message": "Payment Verified Successfully",
                            "order_details": OrderSerializer(order).data,
                        },
                        status=200,
                    )
                elif order.order_type == "Custom Orders":
                    return Response(
                    {
                        "status": 1,
                        "message": "Payment Verified Successfully",
                        "order_details": CustomOrderSerializer(order).data,
                    }

                    )
                elif order.order_type == "Long Distance Orders":
                    return Response(
                        {
                            "status": 1,
                            "message": "Payment Verified Successfully",
                            "order_details": OrderSerializer2(order).data,
                        },
                        status=200,
                    )
                else:
                    return Response(
                        {
                            "status": 1,
                            "message": "Payment Verified Successfully",
                            "order_details": OrderSerializer(order).data,
                        },
                        status=200,
                    )





            except razorpay.errors.SignatureVerificationError:  # type: ignore

                order.order_status = "Failed"

                order.save()
                logger.error(f"Payment Failed for {order.order_ID}: Signature Verification Failed ")
                return Response(
                    {"status": 0, "message": "Payment verification failed"}, status=400
                )
            except Exception as e:

                order.status = "Failed"

                order.save()
                logger.error(f"Payment Verification Failed for {order.order_ID}: {e}")
                return Response(
                    {"status": 0, "message": "Payment verification failed"}, status=400
                )

        except Exception as e:

            payment = Payment.objects.get(razorpay_order_id=razorpay_order_id)

            order = payment.order

            order.order_status = "Failed"

            order.save()

            logger.error(f"Payment Failed for {order.order_ID}: {e}")

            return Response(
                {
                    "status": 0,
                    "message": "An unexpected error occurred.",
                    "exception": str(e),
                },
                status=400,
            )


class PastOrdersListAPIView(APIView):
    def get(self, request, *args, **kwargs):
        user = request.user

        orders = Orders.objects.filter(user_uuid=user).exclude(order_status="New Order").order_by("-created_date")

        serializer = PastOrderListSerializer(orders, many=True).data
        for i in serializer:
            if i["order_type"] == "Pick Up":
                if i["order_status"] == "Delivered":
                    i["order_status"] = "Order collected"
            if i["address_type"] == "home":
                i["address_type"] = "Home"
            elif i["address_type"] == "work":
                i["address_type"] = "Work"
            elif i["address_type"] == "others":
                i["address_type"] = "Others"
        return Response(
            {
                "status": 1,
                "message": "Past orders fetched successfully",
                "orders": serializer,
            },
            status=status.HTTP_200_OK,
        )


class PastOrderDetailAPIView(APIView):
    def get(self, request, order_uuid):
        try:
            order = Orders.objects.get(uuid=order_uuid, user_uuid=request.user)

            if order.order_type == "Local Orders":
                serializer = OrderSerializer(order).data

            elif order.order_type == "Long Distance Orders":
                serializer = OrderSerializer2(order).data
                if serializer.get("order_status") == "Despatched" or serializer.get("order_status") == "Delivered":
                    order_delivery = OrderDelivery.objects.filter(order = order_uuid).first()
                    courier_details = CourierDetailsSerializer(order_delivery).data
                    serializer["courier_details"] = courier_details

            elif order.order_type == "Custom Orders":
                serializer = CustomOrderSerializer(order).data
            elif order.order_type == "Pick Up":
                serializer = OrderSerializer(order).data
                if serializer["order_status"] == "Delivered":
                        serializer["order_status"] = "Order collected"


            else:
                serializer = OrderSerializer(order).data



            return Response(
                {
                    "status": 1,
                    "message": "Order details fetched successfully",
                    "order_details": serializer,
                },
                status=status.HTTP_200_OK,
            )
        except Orders.DoesNotExist:
            return Response(
                {
                    "status": 0,
                    "message": "Error while fetching order details",
                    "exc":str(Exception)
                },
                status=status.HTTP_200_OK,
            )
        

class AddressChangeView(APIView):
    def post(self, request):
        if request.user.is_authenticated:
            user = request.user
            address_id = request.data.get("address_id")
            address = Address.objects.get(id=address_id, user=user)
            user_location = UserLocation.objects.filter(user=user).first()
            shop = None
            if user_location.shop: # type: ignore
                shop = user_location.shop # type: ignore
                new_address_coords = (address.latitude, address.longitude)
                shop_location_coords = (shop.latitude, shop.longitude)
                gmaps = googlemaps.Client(key="AIzaSyBqLt_zwm0JnJR8Rm29_Lqrs0I5P0CkcYU")
                result = gmaps.distance_matrix(origins=new_address_coords, destinations=shop_location_coords, mode="driving") # type: ignore
                if result["rows"][0]["elements"][0]["status"] == 'OK':
                    road_distance = result["rows"][0]["elements"][0]["distance"]["value"]
                    if road_distance <= shop.delivery_radius * 1000:
                        return Response(
                            {
                            "status": 1,
                            "message": "Address change applicable.",
                            "proceed":True
                            },
                        status=200,
                        )
                    else:
                        return Response(
                            {
                                "status": 0,
                                "message": "Address change not possible. Address is outside the delivery radius.",
                                "proceed": False
                            },
                            status=200,
                        )
                else:
                    return Response(
                        {
                            "status": 0,
                            "message": "Address change not possible. Address is outside the delivery radius.",
                            "proceed":False
                        },
                        status=200,
                    )
            else:
                return Response(
                        {
                            "status": 1,
                            "message": "Address change applicable.",
                            "proceed":True
                        },
                        status=200,
                    )




class CustomOrderCheckoutAPIView(APIView):



    def post(self, request, *args, **kwargs):
        try:
            if request.user.is_authenticated:
                user = request.user
                # Get the cart associated with the user
                pickup = request.data.get("pickup", False)
                order_id = request.data.get("order_id", None)
                address = None
                delivery_slot = None

                # check order valid

                order = Orders.objects.filter(order_ID = order_id).first()
                if order.order_status != "Bill Created":
                    return Response(
                        {
                            "status":0,
                            "message": "Payment not allowed!"
                        }
                    )
                if not order:
                    return Response(
                        {
                            "status": 0,
                            "message": "Order not found!",
                        }
                    )

                # If not a pickup, fetch address and delivery slot details
                if not pickup:
                    address_id = request.data.get("address_id")
                    address = get_object_or_404(Address, id=address_id)
                    delivery_slot_id = request.data.get("delivery_slot")
                    delivery_slot = get_object_or_404(DeliverySlot, id=delivery_slot_id)
                    delivery_slot_time = delivery_slot.start_time
                delivery_slot_date = request.data.get("deliver_date")


                # If delivery is requested, create a Dropaddress
                drop_address = None
                if not pickup:
                    drop_address = Dropaddress.objects.create(
                        name=f"{user.first_name} {user.last_name}",
                        house_number_or_name=address.flat_no,  # type: ignore
                        land_mark=address.landmark,  # type: ignore
                        street="street",
                        city="city",
                        state_or_province="state",
                        pin_code=000000,
                        latitude=address.latitude,  # type: ignore
                        longitude=address.longitude,  # type: ignore
                        address_type=address.address_type,  # type: ignore
                        contact_number=user.phone_number,
                    )

                with transaction.atomic():
                    order_update = Orders.objects.filter(order_ID = order_id).update(
                        order_status="New Order",
                        drop_address=drop_address if drop_address else None,
                        delivery_instruction=request.data.get("delivery_instruction", ""),
                        cooking_instruction=request.data.get("cooking_instruction", ""),
                        delivery_slot_date=delivery_slot_date,
                        delivery_slot_time=delivery_slot_time if not pickup else None,

                    )

                    if order_update == 0:
                        raise Exception("Update failed! Rolling back transaction.")

                    razorpay_client = razorpay.Client(
                        auth=(settings.RAZORPAY_KEY_ID, settings.RAZORPAY_KEY_SECRET)
                    )
                    razorpay_order = razorpay_client.order.create(  # type: ignore
                        {
                            "amount": int(order.grand_total * 100),  # amount in paise
                            "currency": "INR",
                            "payment_capture": 1,  # Automatic payment capture
                        }
                    )
                    Payment.objects.create(
                        order=order,
                        razorpay_order_id=razorpay_order["id"],
                        payment_status="pending",  # payment status pending
                    )

                    return Response(
                        {
                            "status": 1,
                            "message": "Order Created Successfully",
                            "data": {
                                "order_id": order.order_ID,
                                "razorpay_order_id": razorpay_order["id"],
                                "razorpay_payment_link": razorpay_order[
                                    "receipt"
                                ],  # URL for the payment page
                                "amount": order.grand_total,
                            },
                        },
                        status=status.HTTP_201_CREATED,
                    )

            else:
                return Response(
                    {
                        "status": 0,
                        "message": "User not Authenticated",
                    }
                )

        except Exception as e:
            return Response(
                {
                    "status": 0,
                    "message": "An unexpected error occurred.",
                    "exception": str(e),
                },
                status=status.HTTP_400_BAD_REQUEST,
            )




class AdListAPIView(APIView):

    def get(self, request):
        try:
            ads = Ads.objects.all()
            ad_serializer = AdSerializer(ads, many= True)
            return Response(
                {
                    "status": 1,
                    "message": "Ad Listed Successfully",
                    "data": {
                        "ads": ad_serializer.data
                    },
                },
                status=status.HTTP_200_OK,
            )

        except Exception as e:
            return Response(
                {
                    "status": 0,
                    "message": "An unexpected error occurred.",
                    "exception": str(e),
                },
                status=status.HTTP_400_BAD_REQUEST,
            )
        

class DiscountDetailView(APIView):
    """
    View to retrieve the details of a selected discount and list the associated products.
    """

    def get(self, request, discount_id):
        try:
            if request.user.is_authenticated:
                # User is authenticated, get user location and shop
                user_location = UserLocation.objects.filter(user=request.user).first()
            else:
                # Anonymous user, get the anonymous ID and location
                anonymous_id = request.data.get("anonymous_id")
                user_location = UserLocation.objects.filter(
                    anonymous_id=anonymous_id
                ).first()


            if user_location and user_location.shop:

                # Shop found, apply shop logic
                shop_id = user_location.shop.uuid
                product_selections = SalesUnitProductSelection.objects.filter(
                    sales_unit__uuid=shop_id
                ).select_related("sku")
                sku_status_map = {
                    selection.sku.id: selection.shop_admin_status  # type: ignore
                    for selection in product_selections
                    if selection.sku
                }


            discount = get_object_or_404(Discount, id=discount_id)
            
            product_info_list = []

            if discount.DiscountOn == 'Category':
                applicable_categories = discount.ApplicableCategory.all()


                products = Products.objects.filter(item_category__in=applicable_categories)


            elif discount.DiscountOn == 'SubCategory':
                applicable_subcategories = discount.ApplicableSubCategory.all()
                products = Products.objects.filter(item_sub_category__in=applicable_subcategories)

            elif discount.DiscountOn == 'Product':
                applicable_products = discount.ApplicableProduct.all()
                products = applicable_products

            elif discount.DiscountOn == 'Sku':
                applicable_skus = discount.ApplicableSku.all()
                products = Products.objects.filter(skus__in=applicable_skus)



            for product in products:
                product_image = ProductImage.objects.filter(product=product).first()
                product_image_url = product_image.image.url if product_image else None

                if user_location and user_location.shop:
                    skus = SKU.objects.filter(
                        product=product, sku_status__in=["Visible", "Out of Stock"]
                    )
                else:
                    skus = SKU.objects.filter(
                        product=product,
                        sku_status__in=["Visible", "Out of Stock"],
                        sku_expiry_duration__gt=30,
                    )

                product_info = {
                    "product_id": product.id,
                    "product_name": product.item_name,
                    "product_type": product.veg_or_non_veg_status,
                    "offer": discount.DiscountPercentage if discount.DiscountPercentage else 0, 
                    "price": product.skus.first().sku_mrp if product.skus.exists() else None,  
                    "offerprice": (Decimal.from_float(product.skus.first().sku_mrp) * (1 - discount.DiscountPercentage / 100)) if product.skus.exists() else None,
                    "product_img": product_image_url,
                    "sku_name": skus.first().sku_name if skus.exists() else None,
                    "sku_quantity": skus.first().sku_quantity if skus.exists() else None,
                    "sku_unit": skus.first().sku_unit if skus.exists() else None,
                    "skus": [
                        {
                            "sku_name": sku.sku_name,
                             "sku_status": sku_status_map.get(sku.id) if user_location and user_location.shop else sku.sku_status,
                        }
                        for sku in skus
                    ],
                    "wishlist": product.id in [wishlist.product.id for wishlist in Wishlist.objects.filter(user=request.user)] if request.user.is_authenticated else False,
                }

                product_info_list.append(product_info)

            return Response(
                {
                    "status": 1,
                    "message": "Discount details and products retrieved successfully.",
                    "discount_name": discount.DiscountName,
                    "discount_code": discount.DiscountCode,
                    "products": product_info_list,
                },
                status=status.HTTP_200_OK,
            )
        except Exception as e:
            return Response(
                {
                    "status": 0,
                    "message": "An unexpected error occurred.",
                    "Exception": str(e),
                },
                status=status.HTTP_400_BAD_REQUEST,
            )
        

class DiscountsListView(APIView):
    """
    View to list all discounts with DiscountName and StandardImage.
    """

    def get(self, request):
        try:
            try:
                discounts = Discount.objects.all()
            except:
                discounts = []
            discounts_data = [
                {
                    'id': discount.id,
                    'DiscountName': discount.DiscountName,
                    'StandardImage': discount.StandardImage.url if discount.StandardImage else None
                }
                for discount in discounts
            ]


            return Response(
                {
                    "status": 1,
                    "message": "Discounts retrieved successfully.",
                    "discounts": discounts_data,
                },
                status=status.HTTP_200_OK,
            )

        except Exception as e:
            return Response(
                {
                    "status": 0,
                    "message": "An unexpected error occurred.",
                    "Exception": str(e),
                },
                status=status.HTTP_400_BAD_REQUEST,
            )


class Customorder_placing(APIView):
    def generate_unique_order_id(self):
        """Generate a unique order ID."""
        while True:
            order_id = f"ID{random.randint(100, 999)}{random.choice(string.ascii_uppercase)}{random.choice(string.ascii_uppercase)}"
            if not Orders.objects.filter(order_ID=order_id).exists():
                return order_id

    def post(self, request, id):
        try:
            custom_product = get_object_or_404(CustomProduct, pk=id)
            description = request.data.get("description")
            message = request.data.get("message")
            # store_uuid = request.data.get("store_uuid")

            if not description or not message:
                return Response({"error": "All fields are required!"}, status=status.HTTP_400_BAD_REQUEST)

            user = request.user
            # location = UserLocation.objects.filter(user=user)
            location = get_object_or_404(UserLocation, user=user)
            if not location:
                return Response({"error": "Please add your location"}, status=status.HTTP_400_BAD_REQUEST)
            shop = location.shop
            if not user.is_authenticated:
                return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED)


            # Create the order
            new_order = Orders.objects.create(
                order_ID=self.generate_unique_order_id(),
                custom_product=custom_product,
                order_type="Custom Orders",
                user_uuid=user,
                store_uuid=shop,
                description=description,
                message=message,
                order_status="Enquiry",
                color_status="White"
            )
            return Response(
                {
                    "status": 1,
                    "message": "Custom order created successfully!",
                    "order_details": CustomOrderSerializer(new_order).data,
                },
                status=200,
            )


        except Exception as e:
            return Response({"status": 0,
                             "message": "An unexpected error occurred.",
                             "error": str(e), },
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class NotificationListView(CartViewMixin, APIView):
    """
    View to list all notification history.
    """
    def get(self, request):
        try:
            if request.user.is_authenticated:



                try:
                    notification_list = NotificationStat.objects.filter(sendTo=request.user).all()

                except:
                    notification_list = []
                notification_list_data=[]
                for notification in notification_list:
                    if notification.Sendmsg == "Bill Created":
                        bill_details = OrderBillDetailsSerializer(notification.OrderID)
                        custom_product = CustomProductSerializer(notification.OrderID.custom_product)

                        notification_list_data.append( {
                        'id': notification.id,
                        'Sendmsg': notification.Sendmsg,
                        'Description': notification.Description,
                        'OrderID': notification.OrderID.order_ID,
                        'order_type':notification.OrderID.order_type,
                        'order_uuid':notification.OrderID.uuid,
                        'date': notification.created_date.strftime("%Y-%m-%d"),
                        'time': notification.created_date.strftime("%H:%M:%S"),
                        'notification_type' : "Bill Created",
                        'product_details' : custom_product.data,
                        'bill_details': bill_details.data,


                    })
                    else:
                        notification_list_data.append({
                        'id': notification.id,
                        'Sendmsg': notification.Sendmsg,
                        'Description': notification.Description,
                        'OrderID': notification.OrderID.order_ID,
                        'order_type':notification.OrderID.order_type,
                        'order_uuid':notification.OrderID.uuid,
                        'date': notification.created_date.strftime("%Y-%m-%d"),
                        'time': notification.created_date.strftime("%H:%M:%S"),
                        'notification_type': "Normal"
                    })



                return Response(
                    {
                        "status": 1,
                        "message": "Notification history retrieved successfully.",
                        "notification_list": notification_list_data,
                    },
                    status=status.HTTP_200_OK,
                )
            else:
                return Response({
                    "status": 0,
                    "message" : "User not Authenticated"

                })

        except Exception as e:
            return Response(
                {
                    "status": 0,
                    "message": "An unexpected error occurred.",
                    "Exception": str(e),
                },
                status=status.HTTP_400_BAD_REQUEST,
            )