import logging
import uuid
from uuid import UUID

from django.db.models import Q, Prefetch
from django.http import Http404
from django.shortcuts import get_object_or_404
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django_filters.rest_framework import DjangoFilterBackend
from django_ratelimit.decorators import ratelimit
from djangorestframework_camel_case.render import CamelCaseJSONRenderer
from drf_spectacular.utils import extend_schema_view
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.renderers import MultiPartRenderer
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from rest_framework_xml.renderers import XMLRenderer
from rest_framework_yaml.renderers import YAMLRenderer

from core.docs.drf.viewsets import (
    ADDRESS_SCHEMA,
    ATTRIBUTE_GROUP_SCHEMA,
    ATTRIBUTE_SCHEMA,
    ATTRIBUTE_VALUE_SCHEMA,
    CATEGORY_SCHEMA,
    FEEDBACK_SCHEMA,
    ORDER_PRODUCT_SCHEMA,
    ORDER_SCHEMA,
    PRODUCT_SCHEMA,
    WISHLIST_SCHEMA,
)
from core.filters import AddressFilter, BrandFilter, CategoryFilter, FeedbackFilter, OrderFilter, ProductFilter
from core.models import (
    Address,
    Attribute,
    AttributeGroup,
    AttributeValue,
    Brand,
    Category,
    Feedback,
    Order,
    OrderProduct,
    Product,
    ProductImage,
    ProductTag,
    PromoCode,
    Promotion,
    Stock,
    Vendor,
    Wishlist,
)
from core.permissions import EvibesPermission
from core.serializers import (
    AddOrderProductSerializer,
    AddressAutocompleteInputSerializer,
    AddressCreateSerializer,
    AddressSerializer,
    AddressSuggestionSerializer,
    AddWishlistProductSerializer,
    AttributeDetailSerializer,
    AttributeGroupDetailSerializer,
    AttributeGroupSimpleSerializer,
    AttributeSimpleSerializer,
    AttributeValueDetailSerializer,
    AttributeValueSimpleSerializer,
    BrandDetailSerializer,
    BrandSimpleSerializer,
    BulkAddOrderProductsSerializer,
    BulkAddWishlistProductSerializer,
    BulkRemoveOrderProductsSerializer,
    BulkRemoveWishlistProductSerializer,
    BuyOrderSerializer,
    BuyUnregisteredOrderSerializer,
    CategoryDetailSerializer,
    CategorySimpleSerializer,
    DoFeedbackSerializer,
    FeedbackDetailSerializer,
    FeedbackSimpleSerializer,
    OrderDetailSerializer,
    OrderProductSimpleSerializer,
    OrderSimpleSerializer,
    ProductDetailSerializer,
    ProductImageDetailSerializer,
    ProductImageSimpleSerializer,
    ProductSimpleSerializer,
    ProductTagDetailSerializer,
    ProductTagSimpleSerializer,
    PromoCodeDetailSerializer,
    PromoCodeSimpleSerializer,
    PromotionDetailSerializer,
    PromotionSimpleSerializer,
    RemoveOrderProductSerializer,
    RemoveWishlistProductSerializer,
    StockDetailSerializer,
    StockSimpleSerializer,
    VendorDetailSerializer,
    VendorSimpleSerializer,
    WishlistDetailSerializer,
    WishlistSimpleSerializer,
)
from core.utils import format_attributes
from core.utils.messages import permission_denied_message
from core.utils.nominatim import fetch_address_suggestions
from evibes.settings import DEBUG
from payments.serializers import TransactionProcessSerializer

logger = logging.getLogger("django")


class EvibesViewSet(ModelViewSet):
    """
    Defines a viewset for managing Evibes-related operations.

    The EvibesViewSet class inherits from ModelViewSet and provides functionality
    for handling actions and operations on Evibes entities. It includes support
    for dynamic serializer classes based on the current action, customizable
    permissions, and rendering formats.

    Attributes:
        action_serializer_classes: Dictionary mapping action names to their specific
            serializer classes.
        additional: Dictionary to hold additional data related to the view.
        permission_classes: List of permission classes applicable to this viewset.
        renderer_classes: List of renderer classes supported for response formatting.

    Methods:
        get_serializer_class(self):
            Returns the serializer class for the current action or the default
            serializer class from the parent ModelViewSet.
    """

    action_serializer_classes: dict = {}
    additional: dict = {}
    permission_classes = [EvibesPermission]
    renderer_classes = [CamelCaseJSONRenderer, MultiPartRenderer, XMLRenderer, YAMLRenderer]

    def get_serializer_class(self):
        return self.action_serializer_classes.get(self.action, super().get_serializer_class())


@extend_schema_view(**ATTRIBUTE_GROUP_SCHEMA)
class AttributeGroupViewSet(EvibesViewSet):
    """
    Represents a viewset for managing AttributeGroup objects.

    Handles operations related to AttributeGroup, including filtering,
    serialization, and retrieval of data. This class is part of the
    application's API layer and provides a standardized way to process
    requests and responses for AttributeGroup data.

    Attributes:
        queryset (QuerySet): QuerySet for retrieving all AttributeGroup objects.
        filter_backends (list): List of filter backends used to process filters
            in requests.
        filterset_fields (list): List of fields on which filtering operations
            can be performed.
        serializer_class (Serializer): Default serializer class used for
            processing AttributeGroup data during non-list view operations.
        action_serializer_classes (dict): Mapping of view actions to their
            specific serializer classes, allowing customization of serialization
            behavior for certain actions.
    """

    queryset = AttributeGroup.objects.all()
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["is_active"]
    serializer_class = AttributeGroupDetailSerializer
    action_serializer_classes = {
        "list": AttributeGroupSimpleSerializer,
    }


@extend_schema_view(**ATTRIBUTE_SCHEMA)
class AttributeViewSet(EvibesViewSet):
    """
    Handles operations related to Attribute objects within the application.

    Provides a set of API endpoints to interact with Attribute data. This class
    manages querying, filtering, and serialization of Attribute objects, allowing
    dynamic control over the data returned, such as filtering by specific fields
    or retrieving detailed versus simplified information depending on the request.

    Attributes:
        queryset: The base QuerySet used to represent the set of Attribute
            objects available to this viewset.
        filter_backends: Defines the backends used for filtering request data,
            enabling query flexibility.
        filterset_fields: A list of model fields that can be filtered via the API.
        serializer_class: Represents the serializer used by default for
            serialization and deserialization of Attribute data.
        action_serializer_classes: A mapping that defines serializers used for
            specific actions, such as returning less detailed data for a `list`
            action.
    """

    queryset = Attribute.objects.all()
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["group", "value_type", "is_active"]
    serializer_class = AttributeDetailSerializer
    action_serializer_classes = {
        "list": AttributeSimpleSerializer,
    }


@extend_schema_view(**ATTRIBUTE_VALUE_SCHEMA)
class AttributeValueViewSet(EvibesViewSet):
    """
    A viewset for managing AttributeValue objects.

    This viewset provides functionality for listing, retrieving, creating, updating, and deleting
    AttributeValue objects. It integrates with Django REST Framework's viewset mechanisms and uses
    appropriate serializers for different actions. Filtering capabilities are provided through the
    DjangoFilterBackend.

    Attributes:
        queryset (QuerySet): The base queryset for AttributeValue objects.
        filter_backends (list): A list of filtering backends applied to the viewset.
        filterset_fields (list): Fields of the model that can be used for filtering.
        serializer_class (Serializer): The default serializer class for the viewset.
        action_serializer_classes (dict): A dictionary mapping action names to their corresponding
            serializer classes.
    """

    queryset = AttributeValue.objects.all()
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["attribute", "is_active"]
    serializer_class = AttributeValueDetailSerializer
    action_serializer_classes = {
        "list": AttributeValueSimpleSerializer,
    }


@extend_schema_view(**CATEGORY_SCHEMA)
class CategoryViewSet(EvibesViewSet):
    """
    Manages views for Category-related operations.

    The CategoryViewSet class is responsible for handling operations related to
    the Category model in the system. It supports retrieving, filtering, and
    serializing category data. The viewset also enforces permissions to ensure
    that only authorized users can access specific data.

    Attributes:
        queryset: The base queryset used to retrieve category data, including
            prefetching related objects such as parents, children, attributes,
            and tags.
        filter_backends: A list of backends for applying filters to the category
            data.
        filterset_class: The filter class used to define filtering behavior for
            the category queryset.
        serializer_class: The default serializer class used for category objects
            when no specific action serializer is applied.
        action_serializer_classes: A dictionary mapping specific viewset actions
            (e.g., "list") to their corresponding serializer classes.

    Methods:
        get_queryset():
            Retrieves the queryset for the viewset, applying permission checks
            and filtering out inactive categories for users without sufficient
            permissions.
    """

    queryset = Category.objects.all().prefetch_related("parent", "children", "attributes", "tags")
    filter_backends = [DjangoFilterBackend]
    filterset_class = CategoryFilter
    serializer_class = CategoryDetailSerializer
    action_serializer_classes = {
        "list": CategorySimpleSerializer,
    }

    def get_queryset(self):
        qs = super().get_queryset()
        if self.request.user.has_perm("core.view_category"):
            return qs
        return qs.filter(is_active=True)


class BrandViewSet(EvibesViewSet):
    """
    Represents a viewset for managing Brand instances.

    This class provides functionality for querying, filtering, and
    serializing Brand objects. It uses Django's ViewSet framework
    to simplify the implementation of API endpoints for Brand objects.

    Attributes:
        queryset: The base queryset containing all Brand instances.
        filter_backends: A list of filtering backends to apply to the
            queryset. The default is [DjangoFilterBackend].
        filterset_class: The filter class used to define the filtering
            logic for this viewset. The default is BrandFilter.
        serializer_class: The default serializer class used for the
            detailed representation of Brand objects. The default is
            BrandDetailSerializer.
        action_serializer_classes: A dictionary mapping specific actions
            to their corresponding serializer classes. The "list" action
            uses the BrandSimpleSerializer class.
    """

    queryset = Brand.objects.all()
    filter_backends = [DjangoFilterBackend]
    filterset_class = BrandFilter
    serializer_class = BrandDetailSerializer
    action_serializer_classes = {
        "list": BrandSimpleSerializer,
    }

    def get_queryset(self):
        queryset = Brand.objects.all()

        if self.request.user.has_perm("view_category"):
            queryset = queryset.prefetch_related("categories")
        else:
            queryset = queryset.prefetch_related(
                Prefetch("categories", queryset=Category.objects.filter(is_active=True))
            )

        return queryset


@extend_schema_view(**PRODUCT_SCHEMA)
class ProductViewSet(EvibesViewSet):
    """
    Manages operations related to the `Product` model in the system.

    This class provides a viewset for managing products, including their filtering, serialization,
    and operations on specific instances. It extends from `EvibesViewSet` to use common
    functionality and integrates with the Django REST framework for RESTful API operations.
    Includes methods for retrieving product details, applying permissions, and accessing
    related feedback of a product.

    Attributes:
        queryset: The base queryset to retrieve `Product` objects with prefetch optimization.
        filter_backends: Specifies the filtering mechanism for the list views.
        filterset_class: Defines the filter class to be used for filtering products.
        serializer_class: The default serializer class for product details.
        action_serializer_classes: Specific serializer mappings for action methods.
        lookup_field: Field representing the object's lookup value in URLs.
        lookup_url_kwarg: Field key used to extract the object's lookup value from URL.

    Methods:
        get_queryset: Retrieves the queryset with user-specific filtering applied.
        get_object: Fetches a single object based on its identifier, applying permissions.
        feedbacks: Fetches feedback associated with a specific product.
    """

    queryset = Product.objects.prefetch_related("tags", "attributes", "stocks", "images").all()
    filter_backends = [DjangoFilterBackend]
    filterset_class = ProductFilter
    serializer_class = ProductDetailSerializer
    action_serializer_classes = {
        "list": ProductSimpleSerializer,
    }
    lookup_field = "lookup_value"
    lookup_url_kwarg = "lookup_value"

    def get_queryset(self):
        qs = super().get_queryset()
        if self.request.user.has_perm("core.view_product"):
            return qs
        return qs.filter(
            is_active=True,
            brand__is_active=True,
            category__is_active=True,
            stocks__vendor__is_active=True,
        )

    def get_object(self):
        queryset = self.filter_queryset(self.get_queryset())
        lookup_value = self.kwargs[self.lookup_url_kwarg]

        obj = None

        try:
            uuid_obj = UUID(lookup_value)
            obj = queryset.filter(uuid=uuid_obj).first()
        except (ValueError, TypeError):
            pass

        if not obj:
            obj = queryset.filter(slug=lookup_value).first()

        if not obj:
            name = "Product"
            raise Http404(f"{name} does not exist: {lookup_value}")

        self.check_object_permissions(self.request, obj)
        return obj

    @action(detail=True, methods=["get"], url_path="feedbacks")
    def feedbacks(self, request, **kwargs):
        lookup_val = kwargs.get(self.lookup_field)
        try:
            product = Product.objects.get(uuid=lookup_val)
            feedbacks = (
                Feedback.objects.filter(order_product__product=product)
                if request.user.has_perm("core.view_feedback")
                else Feedback.objects.filter(order_product__product=product, is_active=True)
            )
            # noinspection PyTypeChecker
            return Response(data=FeedbackDetailSerializer(feedbacks, many=True).data)
        except Product.DoesNotExist:
            name = "Product"
            return Response(status=status.HTTP_404_NOT_FOUND, data={"detail": _(f"{name} does not exist: {uuid}")})


class VendorViewSet(EvibesViewSet):
    """
    Represents a viewset for managing Vendor objects.

    This viewset allows fetching, filtering, and serializing Vendor data.
    It defines the queryset, filter configurations, and serializer classes
    used to handle different actions. The purpose of this class is to
    provide streamlined access to Vendor-related resources through the
    Django REST framework.

    Attributes:
        queryset: A QuerySet containing all Vendor objects.
        filter_backends: A list containing configured filter backends.
        filterset_fields: A list of fields that can be used for filtering
            Vendor records.
        serializer_class: The default serializer class used for this
            viewset.
        action_serializer_classes: A dictionary mapping specific actions
            (e.g., "list") to custom serializer classes for those actions.
    """

    queryset = Vendor.objects.all()
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["name", "markup_percent", "is_active"]
    serializer_class = VendorDetailSerializer
    action_serializer_classes = {
        "list": VendorSimpleSerializer,
    }


@extend_schema_view(**FEEDBACK_SCHEMA)
class FeedbackViewSet(EvibesViewSet):
    """
    Representation of a view set handling Feedback objects.

    This class manages operations related to Feedback objects, including listing,
    filtering, and retrieving details. The purpose of this view set is to provide
    different serializers for different actions and implement permission-based
    handling of accessible Feedback objects. It extends the base `EvibesViewSet`
    and makes use of Django's filtering system for querying data.

    Attributes:
        queryset: The base queryset for Feedback objects used in this view set.
        filter_backends: List of filter backends to apply, specifically
            `DjangoFilterBackend` for this view set.
        filterset_class: Class specifying the filter set used for querying
            Feedback objects.
        serializer_class: Default serializer class used for this view set.
        action_serializer_classes: A dictionary mapping action names to specific
            serializer classes. For example, the "list" action uses
            `FeedbackSimpleSerializer`.
    """

    queryset = Feedback.objects.all()
    filter_backends = [DjangoFilterBackend]
    filterset_class = FeedbackFilter
    serializer_class = FeedbackDetailSerializer
    action_serializer_classes = {
        "list": FeedbackSimpleSerializer,
    }

    def get_queryset(self):
        qs = super().get_queryset()
        if self.request.user.has_perm("core.view_feedback"):
            return qs
        return qs.filter(is_active=True)


@extend_schema_view(**ORDER_SCHEMA)
class OrderViewSet(EvibesViewSet):
    """
    ViewSet for managing orders and related operations.

    This class provides functionality to retrieve, modify, and manage order objects.
    It includes various endpoints for handling order operations such as adding or
    removing products, performing purchases for registered as well as unregistered
    users, and retrieving the current authenticated user's pending orders.

    The ViewSet uses multiple serializers based on the specific action being
    performed and enforces permissions accordingly while interacting with order data.

    Attributes:
        lookup_field (str): Field name used for performing object lookup.
        lookup_url_kwarg (str): URL keyword argument used for object lookup. Defaults
            to `lookup_field`.
        queryset (QuerySet): Default queryset for retrieving order objects, with
            prefetched related order products.
        filter_backends (list): List of backends applied for filtering the queryset.
        filterset_class (type): Filtering class applied to the queryset for request-based
            customizations.
        serializer_class (type): Default serializer used if no specific serializer is
            defined for an action.
        action_serializer_classes (dict): Mapping of actions to their respective serializers.
            Used to determine the serializer dynamically based on the requested action.
        additional (dict): Additional settings for specific actions.

    Methods:
        get_serializer_class: Returns the serializer class based on the specific
            action being requested.
        get_queryset: Adjusts the queryset based on the request user's permissions,
            favoring anonymous or limited query access for unauthenticated users.
        get_object: Retrieves a specific order object based on the lookup value, either
            its UUID or a human-readable ID.
        current: Retrieves the authenticated user's current pending order.
        buy: Processes an order purchase for an authenticated user with optional parameters
            such as balance and payment overrides, promocodes, and billing/shipping addresses.
        buy_unregistered: Processes an order purchase for unauthenticated users with product,
            customer details, and payment information.
        add_order_product: Adds a product, with optional attributes, to an order specified by UUID.
        remove_order_product: Removes a product, with optional attributes, from an order specified
            by UUID.
        bulk_add_order_products: Adds multiple products with optional attributes to an order.
        bulk_remove_order_products: Removes multiple products with optional attributes from
            an order.
    """

    lookup_field = "lookup_value"
    lookup_url_kwarg = "lookup_value"
    queryset = Order.objects.prefetch_related("order_products").all()
    filter_backends = [DjangoFilterBackend]
    filterset_class = OrderFilter
    serializer_class = OrderDetailSerializer
    action_serializer_classes = {
        "list": OrderSimpleSerializer,
        "buy": OrderDetailSerializer,
        "add_order_product": AddOrderProductSerializer,
        "remove_order_product": RemoveOrderProductSerializer,
    }
    additional = {"retrieve": "ALLOW"}

    def get_serializer_class(self):
        return self.action_serializer_classes.get(self.action, super().get_serializer_class())

    def get_queryset(self):
        qs = super().get_queryset()
        user = self.request.user

        if not user.is_authenticated:
            return qs.filter(user__isnull=True)

        if user.has_perm("core.view_order"):
            return qs

        return qs.filter(user=user)

    def get_object(self):
        lookup_val = self.kwargs[self.lookup_field]
        qs = self.get_queryset()

        try:
            uuid.UUID(lookup_val)
            uuid_q = Q(uuid=lookup_val)
        except ValueError:
            uuid_q = Q()

        obj = get_object_or_404(qs, uuid_q | Q(human_readable_id=lookup_val))
        self.check_object_permissions(self.request, obj)
        return obj

    @action(detail=False, methods=["get"], url_path="current")
    def current(self, request):
        if not request.user.is_authenticated:
            raise PermissionDenied(permission_denied_message)
        order = Order.objects.get(user=request.user, status="PENDING")
        return Response(
            status=status.HTTP_200_OK,
            data=OrderDetailSerializer(order).data,
        )

    @action(detail=True, methods=["post"], url_path="buy")
    def buy(self, request, **kwargs):
        serializer = BuyOrderSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        lookup_val = kwargs.get(self.lookup_field)
        try:
            order = Order.objects.get(user=request.user, uuid=lookup_val)
            instance = order.buy(
                force_balance=serializer.validated_data.get("force_balance"),
                force_payment=serializer.validated_data.get("force_payment"),
                promocode_uuid=serializer.validated_data.get("promocode_uuid"),
                shipping_address=serializer.validated_data.get("shipping_address_uuid"),
                billing_address=serializer.validated_data.get("billing_address_uuid"),
                chosen_products=serializer.validated_data.get("chosen_products"),
            )
            match str(type(instance)):
                case "<class 'payments.models.Transaction'>":
                    return Response(status=status.HTTP_202_ACCEPTED, data=TransactionProcessSerializer(instance).data)
                case "<class 'core.models.Order'>":
                    return Response(status=status.HTTP_200_OK, data=OrderDetailSerializer(instance).data)
                case _:
                    raise TypeError(_(f"wrong type came from order.buy() method: {type(instance)!s}"))
        except Order.DoesNotExist:
            name = "Order"
            return Response(status=status.HTTP_404_NOT_FOUND, data={"detail": _(f"{name} does not exist: {uuid}")})

    @action(detail=False, methods=["post"], url_path="buy_unregistered")
    @method_decorator(ratelimit(key="ip", rate="5/h" if not DEBUG else "888/h"))
    def buy_unregistered(self, request):
        serializer = BuyUnregisteredOrderSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        order = Order.objects.create(status="MOMENTAL")
        products = [p["product_uuid"] for p in serializer.validated_data["products"]]
        transaction = order.buy_without_registration(
            products=products,
            promocode_uuid=serializer.validated_data.get("promocode_uuid"),
            customer_name=serializer.validated_data.get("customer_name"),
            customer_email=serializer.validated_data.get("customer_email"),
            customer_phone_number=serializer.validated_data.get("customer_phone_number"),
            billing_customer_address=serializer.validated_data.get("billing_customer_address_uuid"),
            shipping_customer_address=serializer.validated_data.get("shipping_customer_address_uuid"),
            payment_method=serializer.validated_data.get("payment_method"),
        )
        return Response(status=status.HTTP_202_ACCEPTED, data=TransactionProcessSerializer(transaction).data)

    @action(detail=True, methods=["post"], url_path="add_order_product")
    def add_order_product(self, request, **kwargs):
        serializer = AddOrderProductSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        lookup_val = kwargs.get(self.lookup_field)
        try:
            order = Order.objects.get(uuid=lookup_val)
            if not (request.user.has_perm("core.add_orderproduct") or request.user == order.user):
                raise PermissionDenied(permission_denied_message)

            order = order.add_product(
                product_uuid=serializer.validated_data.get("product_uuid"),
                attributes=format_attributes(serializer.validated_data.get("attributes")),
            )
            return Response(status=status.HTTP_200_OK, data=OrderDetailSerializer(order).data)
        except Order.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

    @action(detail=True, methods=["post"], url_path="remove_order_product")
    def remove_order_product(self, request, **kwargs):
        serializer = RemoveOrderProductSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        lookup_val = kwargs.get(self.lookup_field)
        try:
            order = Order.objects.get(uuid=lookup_val)
            if not (request.user.has_perm("core.delete_orderproduct") or request.user == order.user):
                raise PermissionDenied(permission_denied_message)

            order = order.remove_product(
                product_uuid=serializer.validated_data.get("product_uuid"),
                attributes=format_attributes(serializer.validated_data.get("attributes")),
            )
            return Response(status=status.HTTP_200_OK, data=OrderDetailSerializer(order).data)
        except Order.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

    @action(detail=True, methods=["post"], url_path="bulk_add_order_products")
    def bulk_add_order_products(self, request, **kwargs):
        serializer = BulkAddOrderProductsSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        lookup_val = kwargs.get(self.lookup_field)
        try:
            order = Order.objects.get(uuid=lookup_val)
            if not (request.user.has_perm("core.add_orderproduct") or request.user == order.user):
                raise PermissionDenied(permission_denied_message)

            order = order.bulk_add_products(
                products=serializer.validated_data.get("products"),
            )
            return Response(status=status.HTTP_200_OK, data=OrderDetailSerializer(order).data)
        except Order.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

    @action(detail=True, methods=["post"], url_path="bulk_remove_order_products")
    def bulk_remove_order_products(self, request, **kwargs):
        serializer = BulkRemoveOrderProductsSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        lookup_val = kwargs.get(self.lookup_field)
        try:
            order = Order.objects.get(uuid=lookup_val)
            if not (request.user.has_perm("core.delete_orderproduct") or request.user == order.user):
                raise PermissionDenied(permission_denied_message)

            order = order.bulk_remove_products(
                products=serializer.validated_data.get("products"),
            )
            return Response(status=status.HTTP_200_OK, data=OrderDetailSerializer(order).data)
        except Order.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)


@extend_schema_view(**ORDER_PRODUCT_SCHEMA)
class OrderProductViewSet(EvibesViewSet):
    """
    Provides a viewset for managing OrderProduct entities.

    This viewset enables CRUD operations and custom actions specific to the
    OrderProduct model. It includes filtering, permission checks, and
    serializer switching based on the requested action. Additionally, it
    provides a detailed action for handling feedback on OrderProduct
    instances.

    Attributes:
        queryset (QuerySet): The base queryset for OrderProduct objects.
        filter_backends (list): Backends responsible for handling filtering
                                mechanisms.
        filterset_fields (list[str]): Fields available for API filtering.
        serializer_class (Serializer): Default serializer class for CRUD
                                       operations.
        action_serializer_classes (dict[str, Serializer]): Mapping of
            specific actions to their corresponding serializer classes.

    Methods:
        get_queryset: Overrides the default queryset to enforce user
                      permissions.

    Actions:
        do_feedback: Custom action to add, remove, or manage feedback for
                     an OrderProduct instance.
    """

    queryset = OrderProduct.objects.all()
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["order", "product", "status", "is_active"]
    serializer_class = AttributeGroupDetailSerializer
    action_serializer_classes = {
        "list": OrderProductSimpleSerializer,
        "do_feedback": DoFeedbackSerializer,
    }

    def get_queryset(self):
        qs = super().get_queryset()
        user = self.request.user

        if user.has_perm("core.view_orderproduct"):
            return qs

        return qs.filter(user=user)

    @action(detail=True, methods=["post"], url_path="do_feedback")
    def do_feedback(self, request, **kwargs):
        serializer = self.get_serializer(request.data)
        serializer.is_valid(raise_exception=True)
        try:
            order_product = OrderProduct.objects.get(uuid=kwargs.get("pk"))
            if not (request.user.has_perm("core.change_orderproduct") or request.user == order_product.order.user):
                raise PermissionDenied(permission_denied_message)
            feedback = order_product.do_feedback(
                rating=serializer.validated_data.get("rating"),
                comment=serializer.validated_data.get("comment"),
                action=serializer.validated_data.get("action"),
            )
            match serializer.validated_data.get("action"):
                case "add":
                    return Response(data=FeedbackDetailSerializer(feedback).data, status=status.HTTP_201_CREATED)
                case "remove":
                    return Response(status=status.HTTP_204_NO_CONTENT)
                case _:
                    return Response(status=status.HTTP_400_BAD_REQUEST)
        except OrderProduct.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)


class ProductImageViewSet(EvibesViewSet):
    """
    Manages operations related to Product images in the application.

    This class-based view set provides endpoints to manage and access ProductImage
    objects. It supports filtering, serialization, and customized serializers for
    different actions to handle ProductImage data.

    Attributes:
        queryset (QuerySet): A Django QuerySet consisting of all ProductImage
            instances within the system.
        filter_backends (list): A list of filter backends that determine the
            filtering behavior on querysets. Set to [DjangoFilterBackend].
        filterset_fields (list): Fields that can be used for filtering data.
            Includes "product", "priority", and "is_active".
        serializer_class (Serializer): The default serializer class used for
            serializing and deserializing ProductImage data. Set to
            ProductImageDetailSerializer.
        action_serializer_classes (dict): A mapping of action names to specific
            serializer classes. For the "list" action, ProductImageSimpleSerializer
            is used.
    """

    queryset = ProductImage.objects.all()
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["product", "priority", "is_active"]
    serializer_class = ProductImageDetailSerializer
    action_serializer_classes = {
        "list": ProductImageSimpleSerializer,
    }


class PromoCodeViewSet(EvibesViewSet):
    """
    Manages the retrieval and handling of PromoCode instances through various
    API actions.

    This class extends the functionality of the EvibesViewSet to provide a
    customized view set for PromoCode objects. It includes filtering capabilities,
    uses specific serializers for different actions, and limits data access
    based on user permissions. The primary purpose is to enable API operations
    related to PromoCodes while enforcing security and filtering.

    Attributes:
        queryset: A queryset of all PromoCode objects in the database.
        filter_backends: Backend classes responsible for filtering queryset data.
        filterset_fields: Fields supported for filtering PromoCode data.
        serializer_class: The default serializer class used for instances when no
            specific action-based serializer is defined.
        action_serializer_classes: A dictionary mapping specific actions (like
            "list") to their corresponding serializer classes.
    """

    queryset = PromoCode.objects.all()
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["code", "discount_amount", "discount_percent", "start_time", "end_time", "used_on", "is_active"]
    serializer_class = PromoCodeDetailSerializer
    action_serializer_classes = {
        "list": PromoCodeSimpleSerializer,
    }

    def get_queryset(self):
        qs = super().get_queryset()
        user = self.request.user

        if user.has_perm("core.view_promocode"):
            return qs

        return qs.filter(user=user)


class PromotionViewSet(EvibesViewSet):
    """
    Represents a view set for managing promotions.

    This class provides operations to handle retrieval, filtering, and serialization
    of promotion objects. It leverages Django REST framework capabilities such as
    queryset management, filter backends, and serializer customization for handling
    different views or actions efficiently.
    """

    queryset = Promotion.objects.all()
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["name", "discount_percent", "is_active"]
    serializer_class = PromotionDetailSerializer
    action_serializer_classes = {
        "list": PromotionSimpleSerializer,
    }


class StockViewSet(EvibesViewSet):
    """
    Handles operations related to Stock data in the system.

    The StockViewSet class is a viewset that provides methods for retrieving,
    filtering, and serializing Stock data. It uses Django's filter
    backends to enable filtering based on specified fields and supports
    custom serializers for different actions.

    Attributes:
        queryset (QuerySet): A queryset of all Stock objects.
        filter_backends (list): A list of filter backends to be applied.
        filterset_fields (list of str): Fields on which the filtering
            is permitted. These fields include "vendor", "product", "sku",
            and "is_active".
        serializer_class (Serializer): The primary serializer used
            for Stock detail representation.
        action_serializer_classes (dict): A dictionary mapping action names
            to their respective serializers. For the "list" action,
            StockSimpleSerializer is used.
    """

    queryset = Stock.objects.all()
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["vendor", "product", "sku", "is_active"]
    serializer_class = StockDetailSerializer
    action_serializer_classes = {
        "list": StockSimpleSerializer,
    }


@extend_schema_view(**WISHLIST_SCHEMA)
class WishlistViewSet(EvibesViewSet):
    """
    ViewSet for managing Wishlist operations.

    The WishlistViewSet provides endpoints for interacting with a user's wish list,
    allowing for the retrieval, modification, and customization of products within
    the wish list. This ViewSet facilitates functionality such as adding, removing,
    and bulk actions for wishlist products. Permission checks are integrated to
    ensure that users can only manage their own wishlists unless explicit permissions
    are granted.

    Attributes
    ----------
    queryset : QuerySet
        The base queryset for retrieving wishlist objects.
    filter_backends : list
        List of backend filters to apply to the queryset.
    filterset_fields : list
        Fields for which filtering is allowed in queries.
    serializer_class : Serializer
        The default serializer class used for wishlist objects.
    action_serializer_classes : dict
        A map of serializers used for specific actions.

    Methods
    -------
    get_queryset()
        Retrieves the queryset, filtered based on the user's permissions.
    current(request)
        Retrieves the currently authenticated user's wishlist.
    add_wishlist_product(request, **kwargs)
        Adds a product to a specific wishlist.
    remove_wishlist_product(request, **kwargs)
        Removes a product from a specific wishlist.
    bulk_add_wishlist_products(request, **kwargs)
        Adds multiple products to a specific wishlist.
    bulk_remove_wishlist_products(request, **kwargs)
        Removes multiple products from a specific wishlist.
    """

    queryset = Wishlist.objects.all()
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["user", "is_active"]
    serializer_class = WishlistDetailSerializer
    action_serializer_classes = {
        "list": WishlistSimpleSerializer,
    }

    def get_queryset(self):
        qs = super().get_queryset()
        user = self.request.user

        if user.has_perm("core.view_wishlist"):
            return qs

        return qs.filter(user=user)

    @action(detail=False, methods=["get"], url_path="current")
    def current(self, request):
        if not request.user.is_authenticated:
            raise PermissionDenied(permission_denied_message)
        wishlist = Wishlist.objects.get(user=request.user)
        if not request.user == wishlist.user:
            raise PermissionDenied(permission_denied_message)
        return Response(
            status=status.HTTP_200_OK,
            data=WishlistDetailSerializer(wishlist).data,
        )

    @action(detail=True, methods=["post"], url_path="add_wishlist_product")
    def add_wishlist_product(self, request, **kwargs):
        serializer = AddWishlistProductSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        try:
            wishlist = Wishlist.objects.get(uuid=kwargs.get("pk"))
            if not (request.user.has_perm("core.change_wishlist") or request.user == wishlist.user):
                raise PermissionDenied(permission_denied_message)

            wishlist = wishlist.add_product(
                product_uuid=serializer.validated_data.get("product_uuid"),
            )

            return Response(status=status.HTTP_200_OK, data=WishlistDetailSerializer(wishlist).data)
        except Wishlist.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

    @action(detail=True, methods=["post"], url_path="remove_wishlist_product")
    def remove_wishlist_product(self, request, **kwargs):
        serializer = RemoveWishlistProductSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        try:
            wishlist = Wishlist.objects.get(uuid=kwargs.get("pk"))
            if not (request.user.has_perm("core.change_wishlist") or request.user == wishlist.user):
                raise PermissionDenied(permission_denied_message)

            wishlist = wishlist.remove_product(
                product_uuid=serializer.validated_data.get("product_uuid"),
            )

            return Response(status=status.HTTP_200_OK, data=WishlistDetailSerializer(wishlist).data)
        except Wishlist.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

    @action(detail=True, methods=["post"], url_path="bulk_add_wishlist_product")
    def bulk_add_wishlist_products(self, request, **kwargs):
        serializer = BulkAddWishlistProductSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        try:
            wishlist = Wishlist.objects.get(uuid=kwargs.get("pk"))
            if not (request.user.has_perm("core.change_wishlist") or request.user == wishlist.user):
                raise PermissionDenied(permission_denied_message)

            wishlist = wishlist.bulk_add_products(
                product_uuids=serializer.validated_data.get("product_uuids"),
            )

            return Response(status=status.HTTP_200_OK, data=WishlistDetailSerializer(wishlist).data)
        except Order.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

    @action(detail=True, methods=["post"], url_path="bulk_remove_wishlist_product")
    def bulk_remove_wishlist_products(self, request, **kwargs):
        serializer = BulkRemoveWishlistProductSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        try:
            wishlist = Wishlist.objects.get(uuid=kwargs.get("pk"))
            if not (request.user.has_perm("core.change_wishlist") or request.user == wishlist.user):
                raise PermissionDenied(permission_denied_message)

            wishlist = wishlist.bulk_remove_products(
                product_uuids=serializer.validated_data.get("product_uuids"),
            )

            return Response(status=status.HTTP_200_OK, data=WishlistDetailSerializer(wishlist).data)
        except Order.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)


@extend_schema_view(**ADDRESS_SCHEMA)
class AddressViewSet(EvibesViewSet):
    """
    This class provides viewset functionality for managing `Address` objects.

    The AddressViewSet class enables CRUD operations, filtering, and custom actions
    related to address entities. It includes specialized behaviors for different HTTP
    methods, serializer overrides, and permission handling based on the request context.

    Attributes:
        pagination_class: Specifies pagination class for the viewset, set to None.
        filter_backends: List of backend classes for filtering querysets.
        filterset_class: Specifies the filter class for filtering address objects.
        queryset: Default queryset containing all Address objects.
        serializer_class: Default serializer class for address objects.
        additional: Dictionary of additional options for this viewset.
    """

    pagination_class = None
    filter_backends = [DjangoFilterBackend]
    filterset_class = AddressFilter
    queryset = Address.objects.all()
    serializer_class = AddressSerializer
    additional = {"create": "ALLOW"}

    def get_serializer_class(self):
        if self.action == "create":
            return AddressCreateSerializer
        if self.action == "autocomplete":
            return AddressAutocompleteInputSerializer
        return AddressSerializer

    def get_queryset(self):
        if self.request.user.has_perm("core.view_address"):
            return super().get_queryset()

        if self.request.user.is_authenticated:
            return super().get_queryset().filter(user=self.request.user)

        return Address.objects.none()

    def create(self, request, **kwargs):
        create_serializer = AddressCreateSerializer(data=request.data, context={"request": request})
        create_serializer.is_valid(raise_exception=True)

        address_obj = create_serializer.create(create_serializer.validated_data)

        output_serializer = AddressSerializer(address_obj, context={"request": request})

        return Response(
            status=status.HTTP_201_CREATED,
            data=output_serializer.data,
        )

    @action(detail=False, methods=["get"], url_path="autocomplete")
    def autocomplete(self, request):
        serializer = AddressAutocompleteInputSerializer(data=request.query_params)
        serializer.is_valid(raise_exception=True)

        q = serializer.validated_data["q"]
        limit = serializer.validated_data["limit"]

        try:
            suggestions = fetch_address_suggestions(query=q, limit=limit)
            serializer = AddressSuggestionSerializer(suggestions, many=True)
            return Response(
                serializer.data,
                status=status.HTTP_200_OK,
            )
        except Exception as e:
            return Response(
                {"detail": _(f"Geocoding error: {e}")},
                status=status.HTTP_502_BAD_GATEWAY,
            )


class ProductTagViewSet(EvibesViewSet):
    """
    Handles operations related to Product Tags within the application.

    This class provides functionality for retrieving, filtering, and serializing
    Product Tag objects. It supports flexible filtering on specific attributes
    using the specified filter backend and dynamically uses different serializers
    based on the action being performed.

    Attributes:
        queryset: The base queryset containing all ProductTag objects.
        filter_backends: A list of backends used to filter the queryset.
        filterset_fields: Fields available for filtering the queryset. Includes
            'tag_name' for filtering by the name of the tag, and 'is_active' for
            filtering active/inactive tags.
        serializer_class: The default serializer class is used when no specific
            serializer is defined for an action.
        action_serializer_classes: A dictionary mapping specific actions (e.g.,
            'list') to custom serializers. Uses ProductTagSimpleSerializer
            for the 'list' action.
    """

    queryset = ProductTag.objects.all()
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["tag_name", "is_active"]
    serializer_class = ProductTagDetailSerializer
    action_serializer_classes = {
        "list": ProductTagSimpleSerializer,
    }
