# coding: utf-8

from django.core.exceptions import ImproperlyConfigured
from django.utils.functional import cached_property
from rest_framework import mixins

# noinspection PyUnresolvedReferences
from rest_framework.generics import (
    GenericAPIView as _GenericAPIView,
    # import the function from this file just in case
    # we want to override it in future
    get_object_or_404,
)

from procu.api.utils import timed_access_logger
from .metadata.mixins import (
    CreateMetaMixin,
    DestroyMetaMixin,
    ListMetaMixin,
    ReplaceMetaMixin,
    RetrieveMetaMixin,
    UpdateMetaMixin,
)
from .mixins import (
    BumpModelMixin,
    CreateModelMixin,
    ReplaceModelMixin,
    RetrieveModelMixin,
    RevisionMixin,
    UpdateModelMixin,
)


class GenericAPIView(RevisionMixin, BumpModelMixin, _GenericAPIView):
    query_params_serializer_class = None
    output_serializer_class = None

    @classmethod
    def as_view(cls, **initkwargs):
        view = super(GenericAPIView, cls).as_view(**initkwargs)
        return timed_access_logger(view)

    @cached_property
    def object(self):
        return self.get_object()

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

        if request.method != 'OPTIONS':
            try:
                self.post_initial(request)

            except (AttributeError, TypeError):
                pass

    @cached_property
    def query_params(self):
        # URL query parameters

        if self.query_params_serializer_class is None:
            raise ImproperlyConfigured(
                'Cannot access query parameters without '
                '{klass}.query_params_serializer_class property set.'.format(
                    klass=self.__class__.__name__
                )
            )

        config = self.query_params_serializer_class(data=self.request.GET)
        config.is_valid(raise_exception=True)
        return config.validated_data

    def check_permissions(self, request):
        if request.method == 'OPTIONS':
            return
        super().check_permissions(request)

    def check_object_permissions(self, request, obj):
        if request.method == 'OPTIONS':
            return
        super().check_object_permissions(request, obj)

    def get_schema(self, **kwargs):
        return self.get_serializer().meta(**kwargs).asdict()

    def get_output_serializer(self, *args, **kwargs):

        if self.output_serializer_class is not None:
            kwargs['context'] = self.get_serializer_context()
            return self.output_serializer_class(*args, **kwargs)

        current_method = self.request.method
        self.request.method = 'GET'

        serializer = self.get_serializer(*args, **kwargs)

        self.request.method = current_method
        return serializer


class CreateAPIView(CreateMetaMixin, CreateModelMixin, GenericAPIView):
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class ListAPIView(ListMetaMixin, mixins.ListModelMixin, GenericAPIView):
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)


class ListReplaceAPIView(
    ListMetaMixin,
    mixins.ListModelMixin,
    ReplaceMetaMixin,
    ReplaceModelMixin,
    GenericAPIView,
):
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

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


class ReplaceAPIView(ReplaceMetaMixin, ReplaceModelMixin, GenericAPIView):
    def put(self, request, *args, **kwargs):
        return self.replace(request, *args, **kwargs)


class ListCreateAPIView(
    ListMetaMixin,
    CreateMetaMixin,
    mixins.ListModelMixin,
    CreateModelMixin,
    GenericAPIView,
):
    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 ListCreateReplaceAPIView(
    ListMetaMixin,
    CreateMetaMixin,
    ReplaceMetaMixin,
    mixins.ListModelMixin,
    CreateModelMixin,
    ReplaceModelMixin,
    GenericAPIView,
):
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

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

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


class RetrieveAPIView(RetrieveMetaMixin, RetrieveModelMixin, GenericAPIView):
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)


class RetrieveUpdateAPIView(
    RetrieveMetaMixin,
    UpdateMetaMixin,
    RetrieveModelMixin,
    UpdateModelMixin,
    GenericAPIView,
):
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

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


class UpdateAPIView(UpdateMetaMixin, UpdateModelMixin, GenericAPIView):
    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)


class RetrieveUpdateDestroyAPIView(
    RetrieveMetaMixin,
    UpdateMetaMixin,
    DestroyMetaMixin,
    RetrieveModelMixin,
    UpdateModelMixin,
    mixins.DestroyModelMixin,
    GenericAPIView,
):
    def get(self, request, *args, **kwargs):
        return self.retrieve(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)


class DestroyAPIView(
    DestroyMetaMixin, mixins.DestroyModelMixin, GenericAPIView
):
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)
