from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.views import APIView
from rest_framework.response import Response
from utils.integration.auth import PublicAPIIntegrationAuthentication
from .serializers import ProductsSerializer, ProductJSONSerializerWithEDPNumberAsKey
from .models import Products, CustomTwentyPagesPagination
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter
from .filters import (
    ProductsFilter,
)
from integration.permissions import IsKnownClient
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework.exceptions import MethodNotAllowed
from drf_spectacular.utils import extend_schema, OpenApiResponse, OpenApiTypes


class ProductsExternalViewset(viewsets.ModelViewSet):
    queryset = (
        Products.objects.select_related("category", "series")
        .prefetch_related("product_images")
        .order_by("name")
    )
    authentication_classes = [PublicAPIIntegrationAuthentication]
    permission_classes = [IsKnownClient]
    serializer_class = ProductsSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_class = ProductsFilter
    pagination_class = CustomTwentyPagesPagination
    http_method_names = ["get", "put", "post"]

    def get_serializer_context(self):
        context = super().get_serializer_context()
        context["is_price_visible"] = "yes"
        context["is_authenticated"] = True
        return context

    def create(self, request, *args, **kwargs):
        """
        <b>Not allowed for this API</b>
        """
        raise MethodNotAllowed("POST")

    def update(self, request, *args, **kwargs):
        """
        <b>Not allowed for this API</b>
        """
        raise MethodNotAllowed("POST")

    @extend_schema(
        responses={
            200: OpenApiResponse(
                response={
                    "type": "object",
                    "properties": {
                        "count": {"type": "integer", "example": 100},
                        "next": {
                            "type": "string",
                            "format": "uri",
                            "nullable": True,
                            "example": "http://api.example.com/...?page=2",
                        },
                        "previous": {
                            "type": "string",
                            "format": "uri",
                            "nullable": True,
                            "example": None,
                        },
                        "results": {
                            "type": "array",
                            "items": {"type": "string", "example": "123456"},
                        },
                    },
                },
            )
        }
    )
    @action(
        detail=False,
        methods=["GET"],
        url_path="edp-numbers",
    )
    def get_product_edp_numbers(self, request):
        """
        Return paginated list of 20 edp numbers per page.
        """
        queryset = self.get_queryset()
        paginator = self.pagination_class()
        paginated_queryset = paginator.paginate_queryset(queryset, request, view=self)

        serialized_data = self.serializer_class(paginated_queryset, many=True).data
        edp_numbers = []

        for data in serialized_data:
            edp_numbers.append(data["edp_number"])

        return paginator.get_paginated_response(data=edp_numbers)

    @action(
        detail=False,
        methods=["POST"],
        serializer_class=ProductJSONSerializerWithEDPNumberAsKey,
        url_path="update-products",
    )
    def update_products(self, request):
        """
        Update products with a format of:

        ```json
        data: {
            "0000-0000": {
                "price": 100,
                "quantity": 20,
                "is_active": True
            },
            "0000-0001": {
                "price": 150,
                "quantity": 10,
                "is_active": True
            }
        }
        ```
        EDP Number as the keys and inside are price, quantity, and is_active values

        Fields that can be updated as of now are: <b>price</b>, <b>quantity</b>, <b>is_active</b>
        """
        data = request.data
        errors = {}
        updated = []

        def append_errors(edp_number, message):
            if edp_number in errors:
                errors[product.edp_number].append(message)
            else:
                errors[product.edp_number] = [message]

        serializer = self.serializer_class(data=data)
        serializer.is_valid(raise_exception=True)
        json_data = serializer.validated_data.get("data")

        edp_numbers = list(json_data.keys())
        products = Products.objects.filter(edp_number__in=edp_numbers)

        if not products.exists():
            return Response(
                {"message": "No products found with the given EDP numbers."},
                status=status.HTTP_404_NOT_FOUND,
            )

        for product in products:
            has_errors = False
            values = json_data[product.edp_number]

            price = values.get("price", None)
            stock_quantity = values.get("quantity", None)
            is_active = values.get("is_active", None)

            if price is not None:
                if price >= 0:
                    product.price = values.get("price")
                else:
                    has_errors = True
                    error_message = "Price must be greater than or equal to 0."
                    append_errors(product.edp_number, error_message)

            if stock_quantity is not None:
                if stock_quantity >= 0:
                    product.quantity = values.get("quantity")
                else:
                    has_errors = True
                    error_message = "Quantity must be greater than or equal to 0."
                    append_errors(product.edp_number, error_message)

            if is_active is not None:
                product.is_active = is_active

            if has_errors is False:
                updated.append(product.edp_number)
                product.save()

        if len(updated) > 0:
            response_message = f"Updated {len(updated)} product/s successfully."
            response_status = status.HTTP_200_OK
        else:
            response_message = "No products updated with the given EDP Numbers."
            response_status = status.HTTP_400_BAD_REQUEST

        return Response(
            {
                "message": response_message,
                "products_updated": updated,
                "errors": errors,
            },
            status=response_status,
        )
