from rest_framework.response import Response
from rest_framework import status
from rest_framework.decorators import action
from utils.utils import ArchiveUtils
from products.models import Products
from .tasks import archive_or_unarchive_affected_objects_under_archived_main_object


class ArchiveMixin:
    def archive_or_unarchive(self, instance, action="archive"):
        model_name = instance._meta.model_name.title()

        affected_model = None
        affected_objects_id = None

        if model_name == "Categories":
            affected_objects_id = Products.objects.filter(
                category__id=instance.id
            ).values_list("id", flat=True)
            affected_model = "Products"

        if model_name == "Series":
            affected_objects_id = Products.objects.filter(
                series__id=instance.id
            ).values_list("id", flat=True)
            affected_model = "Products"

        # Serialized and add to a custom dictionary to pass it to the shared task
        objects_dictionary = {
            "main_model_name": model_name,
            "main_model_object_id": instance.id,
            "affected_model": affected_model,
            "affected_objects_id": (
                list(affected_objects_id) if affected_objects_id else None
            ),
        }

        try:
            archive_utils = ArchiveUtils(model_object=instance)

            if action == "archive":
                archive_utils.archive_object()
            else:
                archive_utils.unarchive_object()

            if affected_objects_id:
                archive_or_unarchive_affected_objects_under_archived_main_object.delay(
                    affected_objects_dict=objects_dictionary,
                    action=action,
                )

            return {
                "message": f"Successfully {action}d object.",
                "status": status.HTTP_200_OK,
            }
        except ValueError as ve:
            return {"message": str(ve), "status": status.HTTP_400_BAD_REQUEST}
        except ModuleNotFoundError as me:
            return {"message": str(me), "status": status.HTTP_404_NOT_FOUND}
        except Exception as e:
            return {
                "message": f"Unexpected error occured. Try again.",
                "detailed_error": str(e),
                "status": status.HTTP_500_INTERNAL_SERVER_ERROR,
            }

    def archive(self, request, *args, **kwargs):
        instance = self.get_object()

        result = self.archive_or_unarchive(instance=instance, action="archive")

        message = result.get("message")
        action_status = result.get("status")

        response = {"message": message}

        if "detailed_error" in result:
            response["detailed_error"] = result.get("detailed_error")

        return Response(
            response,
            status=action_status,
        )

    def unarchive(self, request, *args, **kwargs):
        instance = self.get_object()

        result = self.archive_or_unarchive(instance=instance, action="unarchive")

        message = result.get("message")
        action_status = result.get("status")

        response = {"message": message}

        if "detailed_error" in result:
            response["detailed_error"] = result.get("detailed_error")

        return Response(
            response,
            status=action_status,
        )


class ArchiveViewSetMixin(ArchiveMixin):
    """
    ModelViewSet Mixin to add an archive/unarchive endpoint.
    """

    @action(detail=True, methods=["GET"], serializer_class=None)
    def archive(self, request, pk=None):
        """
        Archive the object by passing its UUID
        """
        return super().archive(request, pk=pk)

    @action(detail=True, methods=["GET"], serializer_class=None)
    def unarchive(self, request, pk=None):
        """
        Unarchive the object by passing its UUID
        """
        return super().unarchive(request, pk=pk)
