from rest_framework import generics, mixins


class GenericServiceAPIView(generics.GenericAPIView):
    service_class = None
    service = None
    pre_service = None
    dto = None

    def initial(self, request, *args, **kwargs):
        super().initial(request, *args, **kwargs)
        self.attach_service()

    def attach_service(self, *args, **kwargs):
        assert self.service_class is not None, (
            "'%s' should either include a `service_class` attribute."
            % self.__class__.__name__
        )

        self.pre_service = self.service_class(None)

        try:
            dto = getattr(self, "dto")
        except AttributeError:
            dto = None

        self.service = self.service_class(dto)

    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        return {
            "request": self.request,
            "format": self.format_kwarg,
            "view": self,
            "service": self.service,
        }


class CreateAPIView(mixins.CreateModelMixin, GenericServiceAPIView):
    """
    Service-layered view for creating a model instance.
    """

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class ListAPIView(mixins.ListModelMixin, GenericServiceAPIView):
    """
    Service-layered view for listing a queryset.
    """

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)


class RetrieveAPIView(mixins.RetrieveModelMixin, GenericServiceAPIView):
    """
    Service-layered view for retrieving a model instance.
    """

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)


class DestroyAPIView(mixins.DestroyModelMixin, GenericServiceAPIView):
    """
    Service-layered view for deleting a model instance.
    """

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)


class UpdateAPIView(mixins.UpdateModelMixin, GenericServiceAPIView):
    """
    Service-layered view for updating a model instance.
    """

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)


class ListCreateAPIView(
    mixins.ListModelMixin, mixins.CreateModelMixin, GenericServiceAPIView
):
    """
    Service-layered view for listing a queryset or creating a model instance.
    """

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class RetrieveUpdateAPIView(
    mixins.RetrieveModelMixin, mixins.UpdateModelMixin, GenericServiceAPIView
):
    """
    Service-layered view for retrieving, updating a model instance.
    """

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)


class RetrieveDestroyAPIView(
    mixins.RetrieveModelMixin, mixins.DestroyModelMixin, GenericServiceAPIView
):
    """
    Service-layered view for retrieving or deleting a model instance.
    """

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)


class RetrieveUpdateDestroyAPIView(
    mixins.RetrieveModelMixin,
    mixins.UpdateModelMixin,
    mixins.DestroyModelMixin,
    GenericServiceAPIView,
):
    """
    Service-layered view for retrieving, updating or deleting a model instance.
    """

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)
