"""
Mixins providing standard actions for attrs shape-based views.
"""
from rest_framework import status
from rest_framework.response import Response

from mantle import create, update


class CreateModelMixin:
    """Create a model instance from an attrs shape."""

    def create(self, request, *args, **kwargs):
        shape = self.structure(request.data)
        instance = self.perform_create(shape)
        # Re-read via Query to return the persisted state.
        query = self.get_query(
            self.get_queryset().filter(pk=instance.pk)
        )
        data = self.unstructure(query.get())
        return Response(data, status=status.HTTP_201_CREATED)

    def perform_create(self, shape):
        return create(self.get_queryset().model, shape)


class ListModelMixin:
    """List a queryset, returning unstructured shape data."""

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        query = self.get_query(queryset)
        prepared_qs = query.prepare(queryset)

        page = self.paginate_queryset(prepared_qs)
        if page is not None:
            data = [
                self.unstructure(
                    query.converter.structure(query.project(obj), query.shape_class)
                )
                for obj in page
            ]
            return self.get_paginated_response(data)

        data = [
            self.unstructure(
                query.converter.structure(query.project(obj), query.shape_class)
            )
            for obj in prepared_qs
        ]
        return Response(data)


class RetrieveModelMixin:
    """Retrieve a model instance, returning unstructured shape data."""

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        query = self.get_query(
            self.get_queryset().filter(pk=instance.pk)
        )
        data = self.unstructure(query.get())
        return Response(data)


class UpdateModelMixin:
    """Update a model instance from an attrs shape."""

    def update(self, request, *args, **kwargs):
        instance = self.get_object()
        partial = kwargs.pop("partial", False)
        shape = self.structure(request.data)
        instance = self.perform_update(instance, shape, partial=partial)
        query = self.get_query(
            self.get_queryset().filter(pk=instance.pk)
        )
        data = self.unstructure(query.get())
        return Response(data)

    def perform_update(self, instance, shape, partial=False):
        return update(instance, shape)

    def partial_update(self, request, *args, **kwargs):
        kwargs["partial"] = True
        return self.update(request, *args, **kwargs)


class DestroyModelMixin:
    """Destroy a model instance."""

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()
