from app.entities import enums
from django.db import transaction
from rest_framework import viewsets, status
from rest_framework.response import Response
from app.applicationlayer.utils import model_to_dict
from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters import rest_framework as filters
from app.entities.models import Module, EntityLog
from app.applicationlayer.utils import (
    CustomPagination, status_message_response, log_save
)
from django_filters.rest_framework import DjangoFilterBackend
from app.applicationlayer.management.module import serializer
from app.applicationlayer.management.module.table_filters import ModuleFilterSet
from app.helper import decorators
from django.db.models import F
from app.helper import decorators


class ModuleViewSet(viewsets.ModelViewSet):
    queryset = Module.objects.order_by('application', 'parent', 'sort_id')
    serializer_class = serializer.ModuleSerializer
    pagination_class = CustomPagination
    lookup_field = 'code'
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    filterset_class = ModuleFilterSet
    ordering_fields = (
        'name', 'application__name',
        'code', 'component', 'sort_id'
    )
    search_fields = (
        'name', 'application__name',
        'code', 'component', 'sort_id'
    )

    @decorators.rms.module_crate
    @transaction.atomic
    def create(self, request, *args, **kwargs):

        mutable = request.POST._mutable
        request.POST._mutable = True
        

        list_submodules = Module.objects.filter(
                parent=request.data['parent'], sort_id__gt=0
                ).order_by('sort_id')
        new_sort_id = request.data['sort_id']

        # for sorting
        if list_submodules.count() == 0:
            request.data['sort_id'] = 1
        elif int(new_sort_id) > list_submodules.count():
            request.data['sort_id'] = list_submodules.count() + 1
        else:
            list_submodules.filter(
                sort_id__gte=new_sort_id
                ).update(sort_id=F('sort_id') + 1)

        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        message = status_message_response(
                        201, 'success',
                        'New module created', serializer.data
                    )
        return Response(message)

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

        queryset = self.filter_queryset(self.get_queryset())
        queryset = queryset.exclude(id=1)
        page = self.paginate_queryset(queryset)

        if page is not None:
            serializer = self.get_serializer(page, many=True)

            message = status_message_response(
                200,
                'success',
                'list of Module found',
                serializer.data
            )
            return self.get_paginated_response(message)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    @decorators.rms.application_crate
    @transaction.atomic
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()

        list_submodules = Module.objects.filter(
                                parent=request.data['parent'],
                                sort_id__gt=0
                            ).order_by('sort_id')

        old_sort_id = instance.sort_id
        old_parent = instance.parent
        update_sort_id = request.data['sort_id']
        update_parent = request.data['parent']

        # for sorting
        if old_parent != update_parent:
            list_submodules_old = Module.objects.filter(
                                parent=old_parent, 
                                sort_id__gt=0).order_by('sort_id')

            # "sort the (instance) submodule's sort_id"
            if int(old_sort_id) != list_submodules_old.count():
                list_submodules_old.filter(
                    sort_id__gt=old_sort_id).update(
                    sort_id=F('sort_id') - 1
                )
            # "sort the (request) submodule's sort_id"
            if list_submodules.count() == 0:
                request.data['sort_id'] = 1
            elif int(update_sort_id) > list_submodules.count():
                request.data['sort_id'] = list_submodules.count() + 1
            else:
                list_submodules.filter(
                    sort_id__gte=update_sort_id).update(
                        sort_id=F('sort_id') + 1)
        else:
            if int(update_sort_id) > list_submodules.count():
                request.data['sort_id'] = list_submodules.count()
                list_submodules.filter(
                        sort_id__gt=old_sort_id
                    ).update(sort_id=F('sort_id') - 1)
            elif int(update_sort_id) < old_sort_id:
                list_submodules.filter(
                        sort_id__gte=update_sort_id, sort_id__lt=old_sort_id
                    ).update(sort_id=F('sort_id') + 1)
            else:
                list_submodules.filter(
                        sort_id__gt=old_sort_id, sort_id__lte=update_sort_id
                    ).update(sort_id=F('sort_id') - 1)

        serializer = self.get_serializer(instance, data=request.data,
                                            partial=partial)

        serializer.is_valid(raise_exception=True)

        old_instance = model_to_dict(instance)

        self.perform_update(serializer)

        new_instance = serializer.data

        log_save(
            enums.LogEnum.UPDATE.value,
            enums.LogEntitiesEnum.MODULE.value,
            old_instance['id'],
            old_instance,
            new_instance
        )

        message = status_message_response(
            200, 'success', 'Module updated',
            serializer.data
        )
        return Response(message)


    @decorators.error_safe
    @transaction.atomic
    def destroy(self, request, *args, **kwargs):

        instance = self.get_object()
        new_instance = model_to_dict(instance)
        self.perform_destroy(instance)

        log_save(
            enums.LogEnum.DELETED.value,
            enums.LogEntitiesEnum.MODULE.value,
            new_instance['id'],
            new_instance,
            ''
        )

        return Response(status=status.HTTP_204_NO_CONTENT)
