from rest_framework.views import APIView
from rest_framework import viewsets
from app.entities import models

from rest_framework.response import Response
from rest_framework import status, views

from rest_framework.decorators import action

from django.db.models import Q
from rest_framework.exceptions import ValidationError
from django.db import transaction
from app.applicationlayer.utils import (CustomPagination,
                                        status_message_response)

from app.applicationlayer.cms.utils_cr import (
    entity_log_bulk
)
from app.applicationlayer.utils import model_to_dict
from app.entities import enums, models
from django.db.models import Q

from app.applicationlayer.utils import log_save, CustomPagination
from rest_framework.exceptions import ParseError
from app.businesslayer.changerequest.change_request_template import (
    tmp_add_edit_delete
)
from app.applicationlayer.management.account.serializer import ChangeRequestList
from app.applicationlayer.cms.template.approver.serializers import ChangeRequestTemplateApproversSerializer
from app.applicationlayer.cms.template.stakeholder.serializers import ChangeRequestTemplateStakeHoldersSerializer
from app.applicationlayer.cms.template.details.serializers import ChangeRequestTemplateDetailsSerializer
from app.applicationlayer.cms.template.attachment.serializers import ChangeRequestTemplateAttachmentsSerializer
from app.applicationlayer.cms.template.header.serializers import ChangeRequestTemplatesSerializer
from app.applicationlayer.cms.template.header.serializers import ChangeRequestTemplatesSerializerList
from app.applicationlayer.cms.template.header.table_filters import HeaderFilterSet


# from django_filters import rest_framework as filters
# from rest_framework.filters import SearchFilter, OrderingFilter
# from django_filters.rest_framework import DjangoFilterBackend

from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters import rest_framework as filters
from django_filters.rest_framework import DjangoFilterBackend


class ChangeRequestTemplatesViewset(viewsets.ModelViewSet):

    # queryset = models.ChangeRequestTemplateHeader.objects.all()
    queryset = models.ChangeRequestTemplateHeader.objects.select_related(
        'created_by_user', 'created_by_department'
    ).all()
    serializer_class = ChangeRequestTemplatesSerializer
    pagination_class = CustomPagination
    lookup_field = 'template_no'
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    filterset_class = HeaderFilterSet
    ordering_fields = (
        'template_no',
        'requested_to_template_name',
        'requested_to_department__company__name',
        'requested_to_department__name',
        'requested_to_user__name',
        'created',
        'created_by_user__name'
    )
    
    search_fields = (
        "template_no", "requested_to_template_id", "created_by_user__name",
        "created_by_user__code", "created_by_department__name",
        "requested_to_company__name", "requested_to_department__name",
        "requested_to_user__name", "requested_to_template_name",
        "requested_to_objective", "requested_to_priority", "description"
    )

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

        queryset = self.filter_queryset(self.get_queryset())
        queryset = queryset.filter(created_by_user=request.user.code,
                                   is_active=True)
        
        self.serializer_class = ChangeRequestTemplatesSerializerList

        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 Templates found',
                serializer.data
            )

            return self.get_paginated_response(message)

        serializer = self.get_serializer(self.queryset, many=True)

        return Response(
            serializer.data,
            status=status.HTTP_200_OK
        )

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

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

            template_no = self.kwargs['template_no']

            existing_transaction = models.ChangeRequestFormHeader.objects.filter(
                template_no=template_no
            )
            
            if existing_transaction.count() > 0:
                models.ChangeRequestTemplateHeader.objects.filter(
                    template_no=template_no
                ).update(is_active=False)
            else:
                enum_approver = enums.LogEntitiesEnum.ChangeRequestTemplateApprovers.value
                enum_stake = enums.LogEntitiesEnum.ChangeRequestTemplateStakeHolders.value
                enum_attach = enums.LogEntitiesEnum.ChangeRequestTemplateAttachments.value
                enum_detail = enums.LogEntitiesEnum.ChangeRequestTemplateDetails.value
                enum_header = enums.LogEntitiesEnum.ChangeRequestTemplateHeader.value

                approver = models.ChangeRequestTemplateApprovers.objects.filter(
                    template_no=template_no
                )

                if approver.count() > 0:
                    entity_log_bulk(
                        approver, enum_approver,
                        models.ChangeRequestTemplateApprovers
                    )
                
                stake = models.ChangeRequestTemplateStakeHolders.objects.filter(
                    template_no=template_no
                )

                if stake.count() > 0:
                    entity_log_bulk(
                        stake, enum_stake,
                        models.ChangeRequestTemplateStakeHolders
                    )

                attachment = models.ChangeRequestTemplateAttachments.objects.filter(
                    template_no=template_no
                )

                if attachment.count() > 0:
                    entity_log_bulk(
                        attachment, enum_attach,
                        models.ChangeRequestTemplateAttachments
                    )

                details = models.ChangeRequestTemplateDetails.objects.filter(
                    template_no=template_no
                )

                if details.count() > 0:
                    entity_log_bulk(
                        details, enum_detail,
                        models.ChangeRequestTemplateDetails
                    )

                header = models.ChangeRequestTemplateHeader.objects.filter(
                    template_no=template_no
                )

                if header.count() > 0:
                    entity_log_bulk(
                        header, enum_header,
                        models.ChangeRequestTemplateHeader
                    )
                return Response({"message": "Deleted"}, status=status.HTTP_200_OK)

            return Response({"message": "Archived"}, status=status.HTTP_200_OK)
        except Exception as e:
            return Response(e,
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    @transaction.atomic
    def partial_update(self, request, *args, **kwargs):

        partial = kwargs.pop('partial', True)
        instance = self.get_object()

        template_no = kwargs['template_no']

        body_data = request.data
        
        tmp_no_exists = models.ChangeRequestFormHeader.objects.filter(
            template_no=template_no)
        
        if not tmp_no_exists:
            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.ChangeRequestTemplateHeader.value,
                old_instance['id'],
                old_instance,
                new_instance
            )

            tmp_add_edit_delete(
                body_data['tmp_approvers'],
                models.ChangeRequestTemplateApprovers,
                enums.LogEntitiesEnum.ChangeRequestTemplateApprovers.value,
                ChangeRequestTemplateApproversSerializer,
                partial,
                self,
                template_no
            )

            tmp_add_edit_delete(
                body_data['tmp_stakes'],
                models.ChangeRequestTemplateStakeHolders,
                enums.LogEntitiesEnum.ChangeRequestTemplateStakeHolders.value,
                ChangeRequestTemplateStakeHoldersSerializer,
                partial,
                self,
                template_no
            )

            tmp_add_edit_delete(
                body_data['tmp_attachments'],
                models.ChangeRequestTemplateAttachments,
                enums.LogEntitiesEnum.ChangeRequestTemplateAttachments.value,
                ChangeRequestTemplateAttachmentsSerializer,
                partial,
                self,
                template_no
            )

            tmp_add_edit_delete(
                body_data['tmp_details'],
                models.ChangeRequestTemplateDetails,
                enums.LogEntitiesEnum.ChangeRequestTemplateDetails.value,
                ChangeRequestTemplateDetailsSerializer,
                partial,
                self,
                template_no
            )

            serializer = self.get_serializer(instance)

            message = status_message_response(
                200, 'success',
                'Change Request Template successfully updated',
                serializer.data
            )

            return Response(message, status=status.HTTP_200_OK)

        message = status_message_response(
            400, 'failed',
            'Unable to update due to existing transaction',
            ''
        )

        return Response(message, status=status.HTTP_400_BAD_REQUEST)

    @action(
        methods=['GET'], detail=False,
        url_path='archived', url_name='archived'
    )
    def archived(self, request, *args, **kwargs):

        queryset = self.filter_queryset(self.get_queryset())
        queryset = queryset.filter(created_by_user=self.request.user.code,
                                   is_active=False)

        self.serializer_class = ChangeRequestTemplatesSerializerList
        
        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 Archived Templates found',
                serializer.data
            )

            return self.get_paginated_response(message)

        serializer = self.get_serializer(self.queryset, many=True)

        return Response(
            serializer.data,
            status=status.HTTP_200_OK
        )
    
    #restore archived CR Template
    @transaction.atomic
    @action(
        methods=['PATCH'], detail=True,
        url_path='restore', url_name='restore'
    )
    def restore(self, request, *args, **kwargs):
        try:
            kwargs['partial'] = True
            template_no = self.kwargs['template_no']
            instance = models.ChangeRequestTemplateHeader.objects.filter(
                template_no=template_no
            )
            instance.update(is_active=True)
            message = status_message_response(
                200, 'success',
                'Archived Change Request Template restored',
                ''
            )

            return Response(message, status=status.HTTP_200_OK)

        except Exception as e:
            message = status_message_response(
                            500, 'failed',
                            'Request was not able to process' + str(e), []
                        )
            return Response(message,
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class ChangeRequestTemplatePost(APIView):

    @transaction.atomic()
    def post(self, request):
        template_header = request.data

        try:
            data_list_approver = []
            data_list_stake = []
            data_list_attach = []
            data_list_detail = []

            template_header_data = {
                'requested_to_template_name': template_header['requested_to_template_name'],
                'requested_to_template_id': template_header['requested_to_template_id'],
                'requested_to_objective': template_header['requested_to_objective'],
                'requested_to_target_date': template_header['requested_to_target_date'],
                'requested_to_priority': template_header['requested_to_priority'],
                'description': template_header['description'],
                'created_by_department': template_header['created_by_department'],
                'created_by_user': template_header['created_by_user'],
                'requested_to_company': template_header['requested_to_company'],
                'requested_to_department': template_header['requested_to_department'],
                'requested_to_user': template_header['requested_to_user']
            }

            sp1 = transaction.savepoint()  # nothing will save to db
            
            # cr_prefix = request.data['requested_to_template_id'])

            # models.ChangeRequestTemplateHeader.objects.filter(
            #     requested_to_template_id=cr_prefix
            # )

            serializer = ChangeRequestTemplatesSerializer(
                data=template_header_data)

            if serializer.is_valid(raise_exception=True):
                serializer.save()

            tmp_id = serializer.data['template_no']

            # create template approvers
            tmp_approvers = template_header['tmp_approvers']

            for tmp_approver in tmp_approvers:
                tmp_approver['template_no'] = tmp_id
                data_list_approver.append(tmp_approver)

            serializerApprover = ChangeRequestTemplateApproversSerializer(
                data=data_list_approver, many=True)
            
            if serializerApprover.is_valid(raise_exception=True):
                serializerApprover.save()

            # create template stakes
            if template_header['tmp_stakes']:
                tmp_stakes = template_header['tmp_stakes']

                for tmp_stake in tmp_stakes:
                    tmp_stake['template_no'] = tmp_id
                    data_list_stake.append(tmp_stake)

                serializerStake = ChangeRequestTemplateStakeHoldersSerializer(
                    data=data_list_stake, many=True)

                if serializerStake.is_valid(raise_exception=True):
                    serializerStake.save()

            # create template attachments
            if template_header['tmp_attachments']:
                tmp_attachments = template_header['tmp_attachments']

                for tmp_attachment in tmp_attachments:
                    tmp_attachment['template_no'] = tmp_id
                    data_list_attach.append(tmp_attachment)

                serializerAttach = ChangeRequestTemplateAttachmentsSerializer(
                    data=data_list_attach, many=True)

                if serializerAttach.is_valid(raise_exception=True):
                    serializerAttach.save()

            # create template details
            if template_header['tmp_details']:
                tmp_details = template_header['tmp_details']

                for tmp_detail in tmp_details:
                    tmp_detail['template_no'] = tmp_id
                    data_list_detail.append(tmp_detail)

                serializerDetail = ChangeRequestTemplateDetailsSerializer(
                    data=data_list_detail, many=True)
                
                if serializerDetail.is_valid(raise_exception=True):
                    serializerDetail.save()

            message = {
                'code': 201,
                'status': 'success',
                'message': 'Template Details successfully saved!',
                'results': serializer.data
            }

            return Response(message, status=status.HTTP_201_CREATED)

        except ValidationError as e:
            transaction.savepoint_rollback(sp1)
            message = {
                'code': 400,
                'status': 'failed',
                'message': str(e),
            }
            return Response(message, status=status.HTTP_400_BAD_REQUEST)

        except Exception as e:
            transaction.savepoint_rollback(sp1)
            message = {
                'code': 500,
                'status': 'failed',
                'message': str(e),
            }
            return Response(message,
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)
