from django.shortcuts import get_object_or_404

from mayan.apps.acls.models import AccessControlList
from mayan.apps.documents.models import Document
from mayan.apps.documents.permissions import permission_document_view
from mayan.apps.documents.serializers import DocumentSerializer
from mayan.apps.rest_api import generics
from mayan.apps.rest_api.mixins import AsymmetricSerializerViewMixin

from .models import Index, IndexInstance
from .permissions import (
    permission_document_indexing_create, permission_document_indexing_delete,
    permission_document_indexing_edit,
    permission_document_indexing_instance_view,
    permission_document_indexing_view
)
from .serializers import (
    IndexInstanceNodeSerializer, IndexInstanceSerializer,
    IndexTemplateSerializer, IndexTemplateNodeSerializer,
    IndexTemplateNodeWriteSerializer, IndexTemplateWriteSerializer
)


class APIDocumentIndexInstanceNodeListView(generics.ListAPIView):
    """
    Returns a list of all the indexes instance nodes where this document is found.
    """
    mayan_object_permissions = {'GET': (permission_document_indexing_instance_view,)}
    serializer_class = IndexInstanceNodeSerializer

    def get_document(self):
        queryset = AccessControlList.objects.restrict_queryset(
            permission=permission_document_view,
            queryset=Document.objects.all(), user=self.request.user
        )

        return get_object_or_404(
            klass=queryset, pk=self.kwargs['document_id']
        )

    def get_queryset(self):
        return self.get_document().index_instance_nodes.all()


class APIIndexInstanceDetailView(generics.RetrieveAPIView):
    """
    get: Returns the details of the selected index instance.
    """
    lookup_url_kwarg = 'index_instance_id'
    mayan_object_permissions = {'GET': (permission_document_indexing_instance_view,)}
    queryset = IndexInstance.objects.all()
    serializer_class = IndexInstanceSerializer


class APIIndexInstanceListView(generics.ListAPIView):
    """
    get: Returns a list of all the indexes instances.
    """
    mayan_object_permissions = {'GET': (permission_document_indexing_instance_view,)}
    queryset = IndexInstance.objects.all()
    serializer_class = IndexInstanceSerializer


class APIIndexInstanceNodeViewMixin:
    serializer_class = IndexInstanceNodeSerializer

    def get_index_instance(self):
        queryset = AccessControlList.objects.restrict_queryset(
            permission=permission_document_indexing_instance_view,
            queryset=IndexInstance.objects.all(), user=self.request.user
        )

        return get_object_or_404(
            klass=queryset, pk=self.kwargs['index_instance_id']
        )


class APIIndexInstanceNodeListView(
    APIIndexInstanceNodeViewMixin, generics.ListAPIView
):
    """
    get: Returns a list of all the template nodes for the selected index.
    post: Create a new index template node.
    """
    def get_queryset(self):
        return self.get_index_instance().get_children()


class APIIndexInstanceNodeDetailView(
    APIIndexInstanceNodeViewMixin, generics.RetrieveAPIView
):
    """
    delete: Delete the selected index template node.
    get: Returns the details of the selected index template node.
    patch: Partially edit an index template node.
    put: Edit an index template node.
    """
    lookup_url_kwarg = 'index_instance_node_id'

    def get_queryset(self):
        return self.get_index_instance().get_nodes()


class APIIndexInstanceNodeDocumentListView(
    APIIndexInstanceNodeViewMixin, generics.ListAPIView
):
    """
    Returns a list of all the documents contained by a particular index node
    instance.
    """
    mayan_object_permissions = {'GET': (permission_document_view,)}
    serializer_class = DocumentSerializer

    def get_node(self):
        return get_object_or_404(
            klass=self.get_index_instance().get_nodes(),
            pk=self.kwargs['index_instance_node_id']
        )

    def get_queryset(self):
        return self.get_node().documents.all()


class APIIndexTemplateViewMixin(AsymmetricSerializerViewMixin):
    queryset = Index.objects.all()
    read_serializer_class = IndexTemplateSerializer
    write_serializer_class = IndexTemplateWriteSerializer


class APIIndexTemplateListView(
    APIIndexTemplateViewMixin, generics.ListCreateAPIView
):
    """
    get: Returns a list of all the defined indexes template.
    post: Create a new index template.
    """
    mayan_object_permissions = {'GET': (permission_document_indexing_view,)}
    mayan_view_permissions = {'POST': (permission_document_indexing_create,)}
    queryset = Index.objects.all()


class APIIndexTemplateDetailView(
    APIIndexTemplateViewMixin, generics.RetrieveUpdateDestroyAPIView
):
    """
    delete: Delete the selected index template.
    get: Returns the details of the selected index template.
    patch: Partially edit an index template.
    put: Edit an index template.
    """
    lookup_url_kwarg = 'index_template_id'
    mayan_object_permissions = {
        'GET': (permission_document_indexing_view,),
        'PUT': (permission_document_indexing_edit,),
        'PATCH': (permission_document_indexing_edit,),
        'DELETE': (permission_document_indexing_delete,)
    }
    queryset = Index.objects.all()


class APIIndexTemplateNodeViewMixin(AsymmetricSerializerViewMixin):
    object_permissions = {
        'GET': permission_document_indexing_view,
        'PATCH': permission_document_indexing_edit,
        'PUT': permission_document_indexing_edit,
        'POST': permission_document_indexing_edit,
        'DELETE': permission_document_indexing_edit
    }
    read_serializer_class = IndexTemplateNodeSerializer
    write_serializer_class = IndexTemplateNodeWriteSerializer

    def get_serializer(self, *args, **kwargs):
        if not self.request:
            return None

        return super().get_serializer(*args, **kwargs)

    def get_index_template(self):
        if 'index_template_id' in self.kwargs:
            permission = self.object_permissions[self.request.method]

            queryset = AccessControlList.objects.restrict_queryset(
                permission=permission, queryset=Index.objects.all(),
                user=self.request.user
            )

            return get_object_or_404(
                klass=queryset, pk=self.kwargs['index_template_id']
            )

    def get_serializer_context(self):
        context = super().get_serializer_context()
        context['index_template'] = self.get_index_template()
        return context


class APIIndexTemplateNodeListView(
    APIIndexTemplateNodeViewMixin, generics.ListCreateAPIView
):
    """
    get: Returns a list of all the template nodes for the selected index.
    post: Create a new index template node.
    """
    def get_queryset(self):
        return self.get_index_template().template_root.get_children()


class APIIndexTemplateNodeDetailView(
    APIIndexTemplateNodeViewMixin, generics.RetrieveUpdateDestroyAPIView
):
    """
    delete: Delete the selected index template node.
    get: Returns the details of the selected index template node.
    patch: Partially edit an index template node.
    put: Edit an index template node.
    """
    lookup_url_kwarg = 'index_template_node_id'

    def get_queryset(self):
        return self.get_index_template().node_templates.all()
