# coding: utf-8


from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.db.models import Model
from django.db.models.functions import Now

from rest_framework.generics import get_object_or_404
from rest_framework.permissions import SAFE_METHODS
from rest_framework.response import Response
from rest_framework.status import HTTP_201_CREATED, is_success
from reversion import add_meta
from reversion.views import (
    RevisionMixin as _RevisionMixin,
    _request_creates_revision,
)

from procu.api import models
from procu.api.models import RevisionMeta


class MakeDeletedImmutable(object):
    def get_queryset(self):
        qs = super().get_queryset()

        if self.request.method not in SAFE_METHODS:
            qs = qs.filter(is_deleted=False)

        return qs


class BumpModelMixin(object):
    def finalize_response(self, request, response, *args, **kwargs):

        is_write = request.method in ('POST', 'PATCH', 'PUT')

        if is_write and is_success(response.status_code):

            if 'quote_id' in kwargs:
                models.Quote.objects.filter(id=kwargs['quote_id']).update(
                    updated_at=Now()
                )

            if 'enquiry_id' in kwargs:
                models.Enquiry.objects.filter(id=kwargs['enquiry_id']).update(
                    updated_at=Now()
                )

        return super().finalize_response(request, response, *args, **kwargs)


class ReplaceModelMixin(object):
    def replace(self, request, *args, **kwargs):

        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            queryset = page

        serializer = self.get_serializer(queryset, data=request.data, many=True)
        serializer.is_valid(raise_exception=True)
        self.perform_replace(serializer)

        if page is not None:
            return self.get_paginated_response(serializer.data)

        return Response(serializer.data)

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


class CreateModelMixin(object):
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        instance = self.perform_create(serializer)

        if not isinstance(instance, Model):
            raise ImproperlyConfigured(
                '.perform_create has to return a new model instance'
            )

        serializer = self.get_output_serializer(instance)

        return Response(serializer.data, status=HTTP_201_CREATED)

    def perform_create(self, serializer):
        return serializer.save()


class RetrieveModelMixin(object):
    def retrieve(self, request, *args, **kwargs):
        serializer = self.get_serializer(self.object)
        return Response(serializer.data)


class UpdateModelMixin(object):
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(
            instance, data=request.data, partial=partial
        )
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            instance = get_object_or_404(self.get_queryset(), id=instance.id)

        serializer = self.get_output_serializer(instance)

        return Response(serializer.data)

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

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


class RevisionMixin(_RevisionMixin):
    disable_reversion = False

    def initial(self, request, *args, **kwargs):

        if not self.disable_reversion and _request_creates_revision(request):
            add_meta(
                RevisionMeta,
                method=request.method,
                path=request.path,
                version=settings.REVISION_VERSION,
            )

        super().initial(request, *args, **kwargs)
