from rest_framework import mixins, viewsets
from rest_framework.response import Response


class PartialUpdateOnlyMixin:
    """
    Provides PATCH support without exposing full update (PUT).
    """

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs["partial"] = True
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, "_prefetched_objects_cache", None):
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)


class NoPutCRUDViewSet(
    mixins.ListModelMixin,
    mixins.RetrieveModelMixin,
    mixins.CreateModelMixin,
    PartialUpdateOnlyMixin,
    mixins.DestroyModelMixin,
    viewsets.GenericViewSet,
):
    """
    CRUD viewset that intentionally excludes PUT/update.
    """


class NoReadCRUDViewSet(
    mixins.CreateModelMixin,
    PartialUpdateOnlyMixin,
    mixins.DestroyModelMixin,
    viewsets.GenericViewSet,
):
    """
    CRUD viewset that intentionally excludes GET list/retrieve and PUT/update.
    Useful when read operations are served from a unified endpoint.
    """


class NoReadUpsertViewSet(
    mixins.CreateModelMixin,
    PartialUpdateOnlyMixin,
    viewsets.GenericViewSet,
):
    """
    Viewset that supports create and PATCH only (no list/retrieve/delete/PUT).
    Useful when delete/read are handled elsewhere.
    """
