from .models import ProductModel
from rest_framework import serializers
from django.core.paginator import Paginator
from rest_framework.views import APIView
from .models import *
from .serializers import *
from rest_framework import status
from rest_framework.response import Response
from django.db.models import Q, Value
from django.db.models.functions import Coalesce
from django.contrib.postgres.search import TrigramSimilarity
from django.shortcuts import get_object_or_404
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from utils.pagination import StructurePagination
from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes
from rest_framework.permissions import AllowAny
from order.serializers import OrderItemSerailzier
from order.models import OrderModel
from django.db.models import Min, Max, Count, Prefetch
from home.models import ShowCaseSlider
from home.serializers import ShowCaseSliderSerialzier
from order.models import Cart, CartItem
from django.db.models import Min, Max, Value
# class APIView(APIView):
#     def __init__(self, *args, **kwargs):
#         super().__init__(*args, **kwargs)
#         print('here')
#         print(self.permission_classes)
#         if AllowAny in self.permission_classes or not self.permission_classes:
#             print('asdf')
#             self.authentication_classes = []


class AllCategories(APIView):
    serializer_class = MainCategorySerializer
    authentication_classes = []

    @extend_schema(
        # parameters=[
        #     OpenApiParameter(
        #         name="search",
        #         description="Search by category name or description.",
        #         required=False,
        #         type=OpenApiTypes.STR,
        #     )
        # ],
        responses={
            200: MainCategorySerializer(many=True),
            404: OpenApiTypes.OBJECT,
        },
    )
    def get(self, request):
        # search_query = request.query_params.get('search', None)
        # if search_query:
        #     categories = MainCategoryModel.objects.filter(Q(name__icontains=search_query) | Q(slug__icontains=search_query))
        # else:

        # Optimize query with prefetch_related to avoid N+1 queries
        categories = MainCategoryModel.objects.prefetch_related(
            Prefetch(
                'subcategorys',
                queryset=SubCategoryModel.objects.annotate(
                    product_count=Count('products')
                )
            )
        ).all()

        categories_ser = self.serializer_class(
            instance=categories, many=True, context={'request': request})
        return Response(categories_ser.data, status=status.HTTP_200_OK)
class UnitCategorySerializerV2(serializers.ModelSerializer):
    subcategorys = serializers.SerializerMethodField()

    class Meta:
        model = UnitCategoryModel
        fields = ['id', 'name', 'slug', 'icon', 'meta_title',
                  'meta_description', 'image', 'subcategorys']

    def get_subcategorys(self, obj):
        main_categories = obj.maincategorys.all()
        return MainCategorySerializer(main_categories, many=True, context=self.context).data


class AllCategoriesV2(APIView):
    serializer_class = UnitCategorySerializerV2
    authentication_classes = []

    @extend_schema(
        responses={
            200: UnitCategorySerializerV2(many=True),
            404: OpenApiTypes.OBJECT,
        },
    )
    def get(self, request):
        from django.core.cache import cache
        
        # Check cache first
        cache_key = 'all_categories_v2'
        cached_data = cache.get(cache_key)
        if cached_data:
            return Response(cached_data, status=status.HTTP_200_OK)
        
        # Optimize query with prefetch_related to avoid N+1 queries
        unit_categories = UnitCategoryModel.objects.prefetch_related(
            Prefetch(
                'maincategorys',
                queryset=MainCategoryModel.objects.only(
                    'id', 'name', 'slug', 'icon', 'meta_title', 'meta_description', 'image', 'video'
                ).prefetch_related(
                    Prefetch(
                        'subcategorys',
                        queryset=SubCategoryModel.objects.only(
                            'id', 'name', 'slug', 'icon', 'meta_title', 'meta_description', 'image', 'parent_id'
                        ).annotate(
                            product_count=Count('products')
                        )
                    )
                )
            )
        ).only('id', 'name', 'slug', 'icon', 'meta_title', 'meta_description', 'image').all()

        categories_ser = self.serializer_class(
            instance=unit_categories, many=True, context={'request': request})
        
        response_data = categories_ser.data
        # Cache for 10 minutes
        cache.set(cache_key, response_data, 60 * 10)
        
        return Response(response_data, status=status.HTTP_200_OK)


class ProductView(APIView):
    serializer_class = DynamicProductSerializer
    permission_classes = [AllowAny]
    # authentication_classes = []

    def get(self, request, slug):
        # Optimize query with select_related and prefetch_related to avoid N+1 queries
        product = get_object_or_404(
            ProductModel.objects.select_related(
                'category', 'category__parent', 'shop')
            .prefetch_related(
                'variants__product_attributes__attribute_type',
                'variants__in_pack_items',
                'variants__images',
                'variants__details__details',
                'variants__details__detail_category',
                'related_products__variants__product_attributes',
                'related_products__category',
            ),
            slug=slug
        )

        if request.user.is_authenticated:
            cart_obj, _ = Cart.objects.get_or_create(user=request.user)
            # Optimize cart items query - prefetch all related data
            cart_items = cart_obj.items.select_related(
                'product_variant__product'
            ).prefetch_related(
                'product_variant__images',
                'product_variant__product_attributes__attribute_type'
            )
            cart_items_ser = OrderItemSerailzier(
                cart_items, many=True, context={'request': request})
            product_ser_context = {
                'request': request, 'view_type': 'instance', 'cart_items': cart_items_ser.data}
        else:
            product_ser_context = {'request': request, 'view_type': 'instance'}

        product_ser = self.serializer_class(
            instance=product, many=False, context=product_ser_context)
        return Response(product_ser.data, status=status.HTTP_200_OK)


class AllProductsView(APIView):
    serializer_class = DynamicProductSerializer
    pagination_class = StructurePagination
    authentication_classes = []

    @extend_schema(
        parameters=[
            OpenApiParameter(
                name="search",
                description="Search by product name or description.",
                required=False,
                type=OpenApiTypes.STR,
            ),
            # OpenApiParameter(
            #     name="category",
            #     type={'type': 'array', 'items': {'type': 'number'}},
            #     location=OpenApiParameter.QUERY,
            #     required=False,
            #     style='form',
            #     explode=False,
            #     ),
            OpenApiParameter(
                name="category",
                type=OpenApiTypes.STR,
                description="slug category (send it with category type)",
                required=False,
            ),
            # OpenApiParameter(
            #     name="category_type",
            #     type=OpenApiTypes.STR,
            #     required=False,
            #     enum=['sub', 'main'],
            #     ),
            OpenApiParameter(
                name="price_gte",
                description="Filter products with price greater than or equal to this value.",
                required=False,
                type=OpenApiTypes.FLOAT,
            ),
            OpenApiParameter(
                name="price_lte",
                description="Filter products with price less than or equal to this value.",
                required=False,
                type=OpenApiTypes.FLOAT,
            ),
            OpenApiParameter(
                name="sort",
                description=(
                    "Sort results by one of the following fields:\n"
                    "`name`, `-name`, `price`, `-price`, `created_at`, `-created_at`."
                    "\nPrefix with `-` for descending order."
                    "remove the price form sorting templory "
                ),
                required=False,
                type=OpenApiTypes.STR,
            ),
            OpenApiParameter(
                name="limit",
                description="Number of results to return per page (pagination).",
                required=False,
                type=OpenApiTypes.INT,
            ),
            OpenApiParameter(
                name="offset",
                description="The starting position of the results (pagination).",
                required=False,
                type=OpenApiTypes.INT,
            ),
            OpenApiParameter(
                name="in_stock",
                description="Filter products that are in stock (positive stock).",
                required=False,
                type=OpenApiTypes.BOOL,
            ),
            OpenApiParameter(
                name="has_discount",
                description="Filter products that have a discount.",
                required=False,
                type=OpenApiTypes.BOOL,
            )
        ],
        description=(
            "Retrieve products with optional filters and sorting. "
            "Provide a list of category IDs to filter products by those categories and their subcategories."
        ),
        responses={
            200: DynamicProductSerializer(many=True, context={'view_type': 'list'}),
            404: OpenApiTypes.OBJECT,
        },
    )
    def get(self, request):
        try:
            category_slug = request.query_params.get('category')

            # Start with optimized base query
            products = ProductModel.objects.select_related(
                'category',
                'category__parent'
            ).prefetch_related(
                'variants__product_attributes__attribute_type',
                'variants__images',
            )

            if category_slug:
                if 'category' not in category_slug:
                    sub_category = get_object_or_404(
                        SubCategoryModel, slug=category_slug)
                    products = products.filter(category=sub_category)
                else:
                    main_category = get_object_or_404(
                        MainCategoryModel, slug=category_slug)
                    sub_categories = main_category.subcategorys.all()
                    products = products.filter(category__in=sub_categories)

            in_stock = request.query_params.get('in_stock')
            if in_stock is not None:
                if in_stock.lower() == 'true':
                    products = products.filter(
                        variants__in_stock__gt=0
                    ).distinct()
                elif in_stock.lower() != 'false':
                    return Response(
                        {'detail': 'in_stock must be "true" or "false".'},
                        status=status.HTTP_400_BAD_REQUEST
                    )

            # Discount filter
            has_discount = request.query_params.get('has_discount')
            if has_discount is not None:
                if has_discount.lower() == 'true':
                    products = products.filter(
                        variants__discount__gt=0
                    ).distinct()
                elif has_discount.lower() != 'false':
                    return Response(
                        {'detail': 'has_discount must be "true" or "false".'},
                        status=status.HTTP_400_BAD_REQUEST
                    )

            # Search
            search_query = request.query_params.get('search')
            if search_query:
                products = products.annotate(
                    similarity=(
                        TrigramSimilarity('name', search_query) +
                        TrigramSimilarity(
                            Coalesce('description', Value('')),
                            search_query
                        )
                    )
                ).filter(similarity__gt=0.1)

            # Price annotation (IMPORTANT for sorting)
            products = products.annotate(
                min_price=Min('variants__price'),
                max_price=Max('variants__price')
            )

            # Price filters
            price_gte = request.query_params.get('price_gte')
            if price_gte:
                try:
                    products = products.filter(
                        max_price__gte=float(price_gte)
                    )
                except ValueError:
                    return Response(
                        {'detail': 'price_gte must be a number.'},
                        status=status.HTTP_400_BAD_REQUEST
                    )

            price_lte = request.query_params.get('price_lte')
            if price_lte:
                try:
                    products = products.filter(
                        min_price__lte=float(price_lte)
                    )
                except ValueError:
                    return Response({'detail': 'price_lte must be a number.'}, status=status.HTTP_400_BAD_REQUEST)

            # Sorting
            sort_by = request.query_params.get('sort')
            if sort_by == 'newest':
                sort_by = '-created_at'
            elif sort_by == 'oldest':
                sort_by = 'created_at'
            if sort_by in ['name', '-name', 'created_at', '-created_at']:
                products = products.order_by(sort_by)

            elif sort_by in ['price', '-price']:
                products = products.order_by('min_price' if sort_by == 'price' else '-min_price')
            elif search_query:
                products = products.order_by('-similarity', 'name')
            else:
                products = products.order_by('name')

            # Pagination
            paginator = self.pagination_class()
            paginated_products = paginator.paginate_queryset(products, request)
            serializer = self.serializer_class(
                paginated_products, many=True,
                context={'request': request, 'view_type': 'list'}
            )
            return paginator.get_paginated_response(serializer.data)

        except MainCategoryModel.DoesNotExist:
            return Response({"detail": "Main Category not found."}, status=status.HTTP_404_NOT_FOUND)

        except SubCategoryModel.DoesNotExist:
            return Response({"detail": "Sub Category not found."}, status=status.HTTP_404_NOT_FOUND)


class ShowCaseCategoryListView(APIView):
    serializer_class = ShowCaseSliderSerialzier
    permission_classes = [AllowAny]

    def get(self, request):
        categoryes = ShowCaseSlider.objects.all()
        categoryes_ser = self.serializer_class(
            instance=categoryes, many=True, context={'request': request})
        return Response(categoryes_ser.data, status=status.HTTP_200_OK)


class ShowCaseProductsView(APIView):
    serializer_class = DynamicProductSerializer
    pagination_class = StructurePagination
    authentication_classes = []

    @extend_schema(
        parameters=[
            OpenApiParameter(
                name="search",
                description="Search by product name or description.",
                required=False,
                type=OpenApiTypes.STR,
            ),
            OpenApiParameter(
                name="slider_category",
                type=OpenApiTypes.INT,
                required=False,
            ),
            OpenApiParameter(
                name="price_gte",
                description="Filter products with price greater than or equal to this value.",
                required=False,
                type=OpenApiTypes.FLOAT,
            ),
            OpenApiParameter(
                name="price_lte",
                description="Filter products with price less than or equal to this value.",
                required=False,
                type=OpenApiTypes.FLOAT,
            ),
            OpenApiParameter(
                name="sort",
                description=(
                    "Sort results by one of the following fields:\n"
                    "`name`, `-name`, `price`, `-price`, `created_at`, `-created_at`."
                    "\nPrefix with `-` for descending order."
                    "remove the price form sorting templory "
                ),
                required=False,
                type=OpenApiTypes.STR,
            ),
            OpenApiParameter(
                name="limit",
                description="Number of results to return per page (pagination).",
                required=False,
                type=OpenApiTypes.INT,
            ),
            OpenApiParameter(
                name="offset",
                description="The starting position of the results (pagination).",
                required=False,
                type=OpenApiTypes.INT,
            ),
            OpenApiParameter(
                name="in_stock",
                description="Filter products that are in stock (positive stock).",
                required=False,
                type=OpenApiTypes.BOOL,
            ),
            OpenApiParameter(
                name="has_discount",
                description="Filter products that have a discount.",
                required=False,
                type=OpenApiTypes.BOOL,
            )
        ],
        description=(
            "Retrieve products with optional filters and sorting. "
            "Provide a list of category IDs to filter products by those categories and their subcategories."
        ),
        responses={
            200: DynamicProductSerializer(many=True, context={'view_type': 'list'}),
            404: OpenApiTypes.OBJECT,
        },
    )
    def get(self, request):
        try:
            category_id = request.query_params.get('slider_category', None)

            # Start with optimized base query
            products = ProductModel.objects.select_related(
                'category',
                'category__parent'
            ).prefetch_related(
                'variants__product_attributes__attribute_type',
                'variants__images',
            )

            if category_id:
                try:
                    category_id = int(category_id)
                except ValueError:
                    return Response({'detail': 'value error category id should be a number'}, status=status.HTTP_400_BAD_REQUEST)

                slider_category = get_object_or_404(
                    ShowCaseSlider, pk=category_id)

                products = products.filter(
                    variants__slider_category=slider_category).distinct()
            else:
                products = products.filter(
                    variants__slider_category__isnull=False).distinct()

            # Filter by stock status if `in_stock` is specified
            in_stock = request.query_params.get('in_stock', "false") == 'true'
            if in_stock:
                products = products.filter(variants__in_stock__gt=0).distinct()

            # Filter by discount if `has_discount` is specified
            has_discount = request.query_params.get(
                'has_discount', "false") == 'true'
            if has_discount:
                products = products.filter(variants__discount__gt=0).distinct()

            # Search filter
            search_query = request.query_params.get('search', None)
            if search_query:
                products = products.filter(Q(name__icontains=search_query) | Q(
                    description__icontains=search_query))

            # Price filters
            price_gte = request.query_params.get('price_gte', None)
            price_lte = request.query_params.get('price_lte', None)

            products = products.annotate(min_price=Min(
                'variants__price'), max_price=Max('variants__price'))

            if price_gte:
                products = products.filter(max_price__gte=price_gte)
            if price_lte:
                products = products.filter(min_price__lte=price_lte)
            # Sorting
            sort_by = request.query_params.get('sort', None)
            if sort_by in ['name', '-name', 'created_at', '-created_at']:
                products = products.order_by(sort_by)
            else:
                products = products.order_by('name')

            # Pagination
            paginator = self.pagination_class()
            paginated_products = paginator.paginate_queryset(products, request)
            serializer = self.serializer_class(paginated_products, many=True, context={
                                               'request': request, 'view_type': 'slider'})
            return paginator.get_paginated_response(serializer.data)

        except MainCategoryModel.DoesNotExist:
            return Response({"detail": "Category not found."}, status=status.HTTP_404_NOT_FOUND)


class CommentView(APIView):
    serializer_class = CommentSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    pagination_class = StructurePagination

    @extend_schema(
        parameters=[
            OpenApiParameter(
                name="limit",
                description="Number of results to return per page (pagination).",
                required=False,
                type=OpenApiTypes.INT,
            ),
            OpenApiParameter(
                name="offset",
                description="The starting position of the results (pagination).",
                required=False,
                type=OpenApiTypes.INT,
            )
        ],
        responses={
            200: CommentSerializer(many=True),
            404: OpenApiTypes.OBJECT,
        },
    )
    def get(self, request, slug):
        product = get_object_or_404(ProductModel, slug=slug)
        # Optimize comments query to prefetch user data
        comments = product.comments.filter(
            review_status__in=['not_reviwed', 'reviewed_and_confirmed']
        ).select_related('user')
        paginator = self.pagination_class()
        paginated_comments = paginator.paginate_queryset(comments, request)
        comments_ser = self.serializer_class(
            instance=paginated_comments, many=True)
        return paginator.get_paginated_response(comments_ser.data)

    def post(self, request, slug):
        comment_ser = CommentSerializer(data=request.data)
        product = get_object_or_404(ProductModel, slug=slug)
        if comment_ser.is_valid():
            comment_ser.save(product=product, user=request.user)
            return Response(comment_ser.data, status=status.HTTP_201_CREATED)
        return Response(comment_ser.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        comment = get_object_or_404(CommentModel, pk=pk)
        if comment.user == request.user:
            comment.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            return Response({"detail": "شما اجازه ی پاک کردن این کامنت را ندارید"}, status=status.HTTP_403_FORBIDDEN)


class BotProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = ProductModel
        fields = ['pk', 'name']


class BotProductsView(APIView):
    serializer_class = BotProductSerializer

    def get(self, request):
        bot_products = ProductModel.objects.filter(show_in_bot=True)

        if bot_products.exists():
            serialized = self.serializer_class(bot_products, many=True)
            return Response({
                "success": True,
                "products": serialized.data
            })
        else:
            return Response({
                "success": False,
                "products": []
            })


class BotProductDetailView(APIView):
    def get(self, request, pk):
        product = get_object_or_404(ProductModel, pk=pk, show_in_bot=True)

        return Response({
            'name': product.name,
            'banner': product.bot_banner,
            'link': f'https://heymlz.com/product/{product.slug}'
        })


class BotCategorySerializer(serializers.ModelSerializer):
    link = serializers.SerializerMethodField()

    class Meta:
        model = MainCategoryModel
        fields = ['pk', 'name', 'link']

    def get_link(self, obj):
        return f'https://heymlz.com/products/category/{obj.slug}'


class BotCategoryView(APIView):
    serializer_class = BotCategorySerializer

    def get(self, request):
        categories = MainCategoryModel.objects.all()
        categories_ser = self.serializer_class(categories, many=True)

        if categories.exists():
            return Response({
                "success": True,
                "categories":   categories_ser.data
            })
        else:
            return Response({
                "success": False,
                "categories": []
            })
