from django.shortcuts import render

from rest_framework import viewsets as meviewsets
from rest_framework.views import APIView
from app.applicationlayer.cms.form import serializers
from app.entities import models
from app.applicationlayer import paginators

from datetime import datetime
from datetime import timedelta

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

from rest_framework.decorators import action
from django.http import Http404

from django.db.models import Q
import requests
from django.conf import settings
from rest_framework.exceptions import ValidationError
from django.db import transaction, IntegrityError, connection
from app.applicationlayer.utils import QuerySetHelper, status_message_response
from app.businesslayer.changerequest import change_request
from app.applicationlayer.cms.utils_cr import (number_generator,
                                               send_mail_requestor,
                                               next_approver_email,
                                               crhistory_save,
                                               send_mail_vendor,
                                               get_max_batchno,
                                               generate_template_id,
                                               crhistory_create_save)
from app.entities import enums
from app.applicationlayer.utils import model_to_dict
import json
from django.shortcuts import get_object_or_404
from django.db.models import Min
from app.applicationlayer.utils import (CustomPagination,
                                        status_message_response)


APPROVER_MESSAGE = settings.APPROVER_MESSAGE
REQUESTOR_MESSAGE = settings.REQUESTOR_MESSAGE
REQUESTOR_REJECT_MESSAGE = settings.REQUESTOR_REJECT_MESSAGE
VENDOR_ACKNOWLEDGE_MESSAGE = settings.VENDOR_ACKNOWLEDGE_MESSAGE
REQUESTOR_ACKNOWLEDGE_MESSAGE = settings.REQUESTOR_ACKNOWLEDGE_MESSAGE
REQUESTOR_COMPLETION_MESSAGE = settings.REQUESTOR_COMPLETION_MESSAGE
VENDOR_ACCEPTANCE_MESSAGE = settings.VENDOR_ACCEPTANCE_MESSAGE
VENDOR_REJECT_MESSAGE = settings.VENDOR_REJECT_MESSAGE


# Change Request Form Views

class ChangeRequestFormsViewset(meviewsets.ModelViewSet):

    serializer_class = serializers.ChangeRequestFormHeaderSerializer
    # pagination_class = paginators.SimplePageNumberPagination
    pagination_class = CustomPagination
    queryset = models.ChangeRequestFormHeader.objects.all()
    lookup_field = 'form_code'

    def list(self, request, *args, **kwargs):
        self.serializer_class = serializers.ChangeRequestFormHeaderSerializerList
        id_number = self.request.user.code
        
        self.queryset = change_request.list_by_user(id_number)
        
        self.queryset = change_request.filter_base(
            self.queryset,
            request.query_params.get('company_requested_to'),
            request.query_params.get('department_requested_to'),
            request.query_params.get('date_modified_from'),
            request.query_params.get('date_modified_to'),
            request.query_params.get('date_required_from'),
            request.query_params.get('date_required_to'),
            request.query_params.get('form_type'),
        )

        if self.request.query_params.get('search'):
            search_key = self.request.query_params.get('search')
        
            self.queryset = self.queryset.filter(
                Q(requested_to_template_name__icontains=search_key.lower()) |
                Q(requested_to_template_id__icontains=search_key.lower())
            )
        
        self.queryset = self.queryset.order_by('-created')
        self.queryset = QuerySetHelper.Sort(self)

        page = self.paginate_queryset(self.queryset)

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

            message = status_message_response(
                200,
                'success',
                'List of Change Request Form 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
        )
        
        # return super(ChangeRequestFormsViewset, self).list(request)

    @action(detail=False,
            methods=['get'],
            url_path='dashboard',
            name="Dashboard Summary")
    def dashboard_view(self, request):
        id_number = self.request.user.code
        
        self.queryset = change_request.list_by_user(id_number)

        self.queryset = change_request.filter_base(
            self.queryset,
            request.query_params.get('company_requested_to'),
            request.query_params.get('department_requested_to'),
            request.query_params.get('date_modified_from'),
            request.query_params.get('date_modified_to'),
            request.query_params.get('date_required_from'),
            request.query_params.get('date_required_to'),
            request.query_params.get('form_type'),
        )

        pending = self.queryset.filter(
            status__iexact='Pending'
        ).count()

        approved = self.queryset.filter(
            status__iexact='Approved'
        ).count()

        rejected = self.queryset.filter(
            status__iexact='Rejected'
        ).count()

        cancelled = self.queryset.filter(
            status__iexact='Cancelled'
        ).count()

        completed = self.queryset.filter(
            status__iexact='Completed & Accepted'  #Completed
        ).count()

        high = self.queryset.filter(
            requested_to_priority='High'
        ).count()

        normal = self.queryset.filter(
            requested_to_priority='Normal'
        ).count()

        awaiting_filtered = change_request.filter_awaiting(self.queryset, id_number)

        # awaiting = awaiting_filtered.count()
        awaiting = 0

        for awaits in awaiting_filtered:

            if awaits.status.lower() == 'rejected':
                print("rejected")
            elif awaits.status.lower() == 'completed & accepted':
                print("completed & accepted")
            elif awaits.status.lower() == 'cancelled':
                print("cancelled")
            else:
                awaiting = awaiting + 1
        
        overdue_filtered = change_request.filter_overdue(self.queryset)

        overdue = overdue_filtered.count()
        
        message = {
            'account_no': id_number,
            'pending': pending,
            'approved': approved,
            'rejected': rejected,
            'cancelled': cancelled,
            'completed': completed,
            'high': high,
            'normal': normal,
            'awaiting': awaiting,
            'overdue': overdue,
            'code': 200,
            'status': 'success',
            'message': 'Dashboard Summary'
        }
       
        return Response(message, status=status.HTTP_200_OK)        

    @action(detail=False,
            methods=['get'],
            url_path='status',
            name="Dashboard Summary Status")
    def list_by_status_view(self, request):

        id_number = self.request.user.code

        self.queryset = change_request.list_by_user(id_number)

        self.queryset = change_request.filter_status(
            self.queryset,
            request.query_params.get('status')
        )

        self.queryset = change_request.filter_base(
            self.queryset,
            request.query_params.get('company_requested_to'),
            request.query_params.get('department_requested_to'),
            request.query_params.get('date_modified_from'),
            request.query_params.get('date_modified_to'),
            request.query_params.get('date_required_from'),
            request.query_params.get('date_required_to'),
            request.query_params.get('form_type'),
        )
        
        self.queryset = self.queryset.order_by('-created')
        self.queryset = QuerySetHelper.Sort(self)
        return super(ChangeRequestFormsViewset, self).list(request)
    
    
    @action(detail=False,
            methods=['get'],
            url_path='overdue',
            name="Dashboard Summary Overdue")
    def list_by_overdue_view(self, request):

        id_number = self.request.user.code

        self.queryset = change_request.list_by_user(id_number)

        self.queryset = change_request.filter_overdue(self.queryset)

        self.queryset = change_request.filter_base(
            self.queryset,
            request.query_params.get('company_requested_to'),
            request.query_params.get('department_requested_to'),
            request.query_params.get('date_modified_from'),
            request.query_params.get('date_modified_to'),
            request.query_params.get('date_required_from'),
            request.query_params.get('date_required_to'),
            request.query_params.get('form_type'),
        )

        self.queryset = self.queryset.order_by('-created')
        self.queryset = QuerySetHelper.Sort(self)
        return super(ChangeRequestFormsViewset, self).list(request)


    @action(detail=False,
            methods=['get'],
            url_path='awaiting',
            name="Dashboard Summary Awaiting")
    def list_by_awaiting_view(self, request):

        id_number = self.request.user.code

        self.queryset = change_request.list_by_user(id_number)

        self.queryset = change_request.filter_awaiting(self.queryset, id_number.code)

        self.queryset = change_request.filter_base(
            self.queryset,
            request.query_params.get('company_requested_to'),
            request.query_params.get('department_requested_to'),
            request.query_params.get('date_modified_from'),
            request.query_params.get('date_modified_to'),
            request.query_params.get('date_required_from'),
            request.query_params.get('date_required_to'),
            request.query_params.get('form_type'),
        )

        self.queryset = self.queryset.filter(
            ~Q(status='Rejected') | 
            ~Q(status='Completed & Accepted') | 
            ~Q(status='Cancelled'))
        
        self.queryset = self.queryset.order_by('-created')
        self.queryset = QuerySetHelper.Sort(self)
        return super(ChangeRequestFormsViewset, self).list(request)


    def retrieve(self, request, *args, **kwargs):
        instance = self.queryset.filter(
            form_code=str(self.kwargs.get('form_code'))
        ).first()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

    @action(
        methods=['PATCH'], detail=True,
        url_path='re_route', url_name='re_route'
    )
    def re_route(self, request, *args, **kwargs):
        
        # generate batchno history
        batchno = get_max_batchno("batch")

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

        form_code = kwargs['form_code']

        status_update = {"status": 'Pending'}
        
        serializer = self.get_serializer(instance,
                                         data=status_update,
                                         partial=partial)
        
        serializer.is_valid(raise_exception=True)
        old_instance = model_to_dict(instance)
        self.perform_update(serializer)

        form_header = get_object_or_404(models.ChangeRequestFormHeader,
                                        pk=instance.id)
        
        new_instance = model_to_dict(form_header)

        # save history in form header
        crhistory_save(
            batchno,
            enums.CREnum.REROUTE.value,
            enums.CREnum.UPDATE.value,
            enums.CREntitiesEnum.CR_FRM_HEADER.value,
            form_code,
            old_instance,
            new_instance
        )
        
        approver_data = []
        # get all approvers of form
        approvers = models.ChangeRequestFormApprovers.objects.filter(
                    form_code=form_code)
        
        for approver in approvers:
            
            approver_add = {
                'id': approver.id,
                'action': None,
                'remarks': None,
                'date_sent': None,
                'action_date': None
            }

            approver_data.append(approver_add)

        change_request.form_add_edit_delete(
            approver_data,
            models.ChangeRequestFormApprovers,
            enums.CREntitiesEnum.CR_FRM_APPROVER.value,
            serializers.ChangeRequestFormApproversSerializer,
            partial,
            self,
            form_code,
            batchno,
            enums.CREnum.REROUTE.value
        )

        min_level = models.ChangeRequestFormApprovers.objects.filter(
            form_code=form_code
        ).aggregate(Min('level'))
        
        min_level = min_level.get('level__min')

        next_approver_email(form_code, min_level)
        
        # update next approver details
        models.ChangeRequestFormApprovers.objects.filter(
            Q(form_code=form_code) & Q(level=str(min_level))
            ).update(
                date_sent=datetime.now()
            )

        message = status_message_response(
            200, 'success',
            'Change request form successfully re routed',
            serializer.data
        )
        return Response(message, status=status.HTTP_200_OK)
    
    @action(
        methods=['PATCH'], detail=True,
        url_path='re_submit', url_name='re_submit'
    )
    def re_submit(self, request, *args, **kwargs):
        
        # generate batchno history
        batchno = get_max_batchno("batch")
        
        # partial update
        partial = kwargs.pop('partial', True)
        instance = self.get_object()

        form_code = kwargs['form_code']

        # get prefix from template header
        frm = models.ChangeRequestFormHeader.objects.get(
            form_code=form_code)
        tmp_prefix = models.ChangeRequestTemplateHeader.objects.get(
            template_no=frm.template_no.template_no
        )

        data_update = {
            "status": 'Draft',
            "created": datetime.now(),
            "requested_to_template_id": tmp_prefix.requested_to_template_id
        }
        
        serializer = self.get_serializer(instance,
                                         data=data_update,
                                         partial=partial)
        
        serializer.is_valid(raise_exception=True)
        old_instance = model_to_dict(instance)
        self.perform_update(serializer)

        form_header = get_object_or_404(models.ChangeRequestFormHeader,
                                        pk=instance.id)

        new_instance = model_to_dict(form_header)

        # save history in form header
        crhistory_save(
            batchno,
            enums.CREnum.RESUBMIT.value,
            enums.CREnum.UPDATE.value,
            enums.CREntitiesEnum.CR_FRM_HEADER.value,
            form_code,
            old_instance,
            new_instance
        )
        
        approver_data = []
        # get all approvers of form
        approvers = models.ChangeRequestFormApprovers.objects.filter(
                    form_code=form_code)
        
        for approver in approvers:
            
            approver_add = {
                'id': approver.id,
                'action': None,
                'remarks': None,
                'date_sent': None,
                'action_date': None,
                'created': datetime.now()
            }

            approver_data.append(approver_add)

        change_request.form_add_edit_delete(
            approver_data,
            models.ChangeRequestFormApprovers,
            enums.CREntitiesEnum.CR_FRM_APPROVER.value,
            serializers.ChangeRequestFormApproversSerializer,
            partial,
            self,
            form_code,
            batchno,
            enums.CREnum.RESUBMIT.value
        )

        message = status_message_response(
            200, 'success',
            'Change request form successfully re submitted',
            serializer.data
        )

        return Response(message, status=status.HTTP_200_OK)

    # actions
    @action(
        methods=['PATCH'], detail=False,
        url_path='actions', url_name='actions'
    )
    def actions(self, request, *args, **kwargs):
        
        # get request data
        id = request.data['id']
        # current_user = request.data['user']
        current_user = self.request.user.code
        form_code = request.data['form_code']
        delegation = request.data['delegation']
        action = request.data['action']
        level = request.data['level']
        next_level = int(request.data['level']) + 1
        remarks = request.data['remarks']
        
        # generate batchno history
        batchno = get_max_batchno("batch")
        
        action_data = {
            'id': int(request.data['id']),
            'action': action,
            'remarks': remarks,
            'action_date': datetime.now()
        }

        approver_instance = models.ChangeRequestFormApprovers.objects.get(
            pk=id
        )

        serializer = serializers.ChangeRequestFormApproversSerializer(
                                        approver_instance,
                                        data=action_data,
                                        partial=True)
        
        serializer.is_valid(raise_exception=True)
        old_instance = model_to_dict(approver_instance)
        
        self.perform_update(serializer)
        new_instance = serializer.data
        
        crhistory_save(
            batchno,
            enums.CREnum.ACTION.value,
            enums.CREnum.UPDATE.value,
            enums.CREntitiesEnum.CR_FRM_APPROVER.value,
            form_code,
            old_instance,
            new_instance
        )

        if action.lower() == 'approved':
            
            # NOTIF MSG FOR REQUESTOR
            requestor_notification_msg = REQUESTOR_MESSAGE.split(';')[0]
            
            # NOTIF MSG FOR NEXT APPROVER
            notification_msg = APPROVER_MESSAGE.split(';')[0]
            
            # SEND EMAIL AND NOTIF TO REQUESTOR
            send_mail_requestor(
                current_user, form_code, delegation,
                requestor_notification_msg, action,
                remarks, level
            )

            next_approver_email(form_code, next_level)
            
            # update next approver details
            models.ChangeRequestFormApprovers.objects.filter(
                Q(form_code=form_code) & Q(level=str(next_level))
                ).update(
                    date_sent=datetime.now()
                )
            
            the_next_vendor = models.ChangeRequestFormApprovers.objects.filter(
                form_code=form_code, level=str(next_level),
                delegation__name='Vendor/Implementor'
            ).first()
            
            if the_next_vendor:
                the_last_vendor = models.ChangeRequestFormApprovers.objects.filter(
                    form_code=form_code, delegation__name='Vendor/Implementor'
                ).order_by('level').last()

                if the_next_vendor == the_last_vendor:
                    models.ChangeRequestFormHeader.objects.filter(
                        form_code=form_code
                    ).update(status='Approved')

        elif action.lower() == 'rejected':
            
            # send email to vendor
            if delegation.lower() == 'requestor':
                notification_msg = VENDOR_REJECT_MESSAGE.split(';')[0]
                
                send_mail_vendor(
                    current_user, form_code, delegation,
                    notification_msg, action,
                    remarks, level
                )
            
            else:
                # EMAIL CODE FOR REQUESTOR
                requestor_notification_msg = REQUESTOR_REJECT_MESSAGE.split(';')[0]
                
                # SEND EMAIL AND NOTIF TO REQUESTOR
                send_mail_requestor(
                    current_user, form_code, delegation,
                    requestor_notification_msg, action,
                    remarks, level
                )
            
            models.ChangeRequestFormHeader.objects.filter(
                form_code=form_code
            ).update(status='Rejected')

        elif action.lower() == 'completed':

            # EMAIL CODE FOR REQUESTOR
            requestor_notification_msg = REQUESTOR_COMPLETION_MESSAGE.split(';')[0]
            
            send_mail_requestor(
                current_user, form_code, delegation,
                requestor_notification_msg, action,
                remarks, level
            )

            models.ChangeRequestFormApprovers.objects.filter(
                Q(level=str(next_level))
                ).update(
                    date_sent=datetime.now()
            )

            models.ChangeRequestFormHeader.objects.filter(
                form_code=form_code
            ).update(status='Completed')

        elif action.lower() == 'acknowledged':

            # EMAIL CODE FOR REQUESTOR
            requestor_notification_msg = REQUESTOR_ACKNOWLEDGE_MESSAGE.split(';')[0]
            
            send_mail_requestor(
                current_user, form_code, delegation,
                requestor_notification_msg, action,
                remarks, level
            )

            models.ChangeRequestFormHeader.objects.filter(
                form_code=form_code
            ).update(status='Acknowledged')

        elif action.lower() == 'accepted':

            # EMAIL CODE FOR VENDOR
            requestor_notification_msg = VENDOR_ACCEPTANCE_MESSAGE.split(';')[0]

            send_mail_vendor(
                current_user, form_code, delegation,
                requestor_notification_msg, action,
                remarks, level
            )

            models.ChangeRequestFormHeader.objects.filter(
                form_code=form_code
            ).update(status='Completed & Accepted')

        elif action.lower() == 'cancelled':
            
            # changed form status to cancelled

            models.ChangeRequestFormHeader.objects.filter(
                form_code=form_code).update(status='Cancelled')

        
        message = status_message_response(
            200, 'success',
            'Action performed',
            serializer.data
        )

        return Response(message, status=status.HTTP_200_OK)

    @transaction.atomic
    @action(
        methods=['PATCH'], detail=True,
        url_path='save', url_name='save'
    )
    def save(self, request, *args, **kwargs):
        
        partial = kwargs.pop('partial', True)
        instance = self.get_object()

        form_code = kwargs['form_code']
        form_data = request.data

        # generate batchno history
        batchno = get_max_batchno("batch")

        # update form header
        serializer = self.get_serializer(instance,
                                         data=form_data,
                                         partial=partial)
        
        serializer.is_valid(raise_exception=True)
        old_instance = model_to_dict(instance)
        self.perform_update(serializer)
        new_instance = serializer.data
        
        # save history in form header
        crhistory_save(
            batchno,
            enums.CREnum.SAVE.value,
            enums.CREnum.UPDATE.value,
            enums.CREntitiesEnum.CR_FRM_HEADER.value,
            form_code,
            old_instance,
            new_instance
        )

        change_request.form_add_edit_delete(
                form_data['frm_approvers'],
                models.ChangeRequestFormApprovers,
                enums.CREntitiesEnum.CR_FRM_APPROVER.value,
                serializers.ChangeRequestFormApproversSerializer,
                partial,
                self,
                form_code,
                batchno,
                enums.CREnum.SAVE.value
        )
        
        change_request.form_add_edit_delete(
                form_data['frm_stakes'],
                models.ChangeRequestFormStakeHolders,
                enums.CREntitiesEnum.CR_FRM_STAKE.value,
                serializers.ChangeRequestFormStakeHoldersSerializer,
                partial,
                self,
                form_code,
                batchno,
                enums.CREnum.SAVE.value
        )

        change_request.form_add_edit_delete(
                form_data['frm_attachments'],
                models.ChangeRequestFormAttachments,
                enums.CREntitiesEnum.CR_FRM_ATTACHMENT.value,
                serializers.ChangeRequestFormAttachmentsSerializer,
                partial,
                self,
                form_code,
                batchno,
                enums.CREnum.SAVE.value
        )

        change_request.form_add_edit_delete(
                form_data['frm_details'],
                models.ChangeRequestFormDetails,
                enums.CREntitiesEnum.CR_FRM_DETAIL.value,
                serializers.ChangeRequestFormDetailsSerializer,
                partial,
                self,
                form_code,
                batchno,
                enums.CREnum.SAVE.value
        )
        
        serializer = self.get_serializer(instance)

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

        return Response(message, status=status.HTTP_200_OK)
        
    @transaction.atomic
    @action(
        methods=['PATCH'], detail=True,
        url_path='submit', url_name='submit'
    )
    def submit(self, request, *args, **kwargs):
        
        partial = kwargs.pop('partial', True)
        instance = self.get_object()

        form_code = kwargs['form_code']
        form_data = request.data

        # generate batchno history
        batchno = get_max_batchno("batch")
        
        # generate requested template id
        requested_to_template_id = generate_template_id(
                                    form_data['requested_to_template_id'],
                                    form_code,
                                    form_data['template_no'])
        
        data_update = {
            "status": 'Pending',
            "created": datetime.now(),
            "requested_to_template_id": requested_to_template_id
        }

        data = {**form_data, **data_update}

        # update form header
        serializer = self.get_serializer(instance,
                                         data=data,
                                         partial=partial)

        serializer.is_valid(raise_exception=True)
        old_instance = model_to_dict(instance)
        self.perform_update(serializer)

        form_header = get_object_or_404(models.ChangeRequestFormHeader,
                                        pk=instance.id)
        
        new_instance = model_to_dict(form_header)
        
        # save history in form header
        crhistory_create_save(
                    batchno,
                    enums.CREnum.SUBMIT.value,
                    enums.CREnum.UPDATE.value,
                    enums.CREntitiesEnum.CR_FRM_HEADER.value,
                    form_code,
                    old_instance,
                    new_instance,
                    requested_to_template_id,
                    form_data['template_no'])

        change_request.form_add_edit_delete(
                form_data['frm_approvers'],
                models.ChangeRequestFormApprovers,
                enums.CREntitiesEnum.CR_FRM_APPROVER.value,
                serializers.ChangeRequestFormApproversSerializer,
                partial,
                self,
                form_code,
                batchno,
                enums.CREnum.SUBMIT.value
        )
        
        change_request.form_add_edit_delete(
                form_data['frm_stakes'],
                models.ChangeRequestFormStakeHolders,
                enums.CREntitiesEnum.CR_FRM_STAKE.value,
                serializers.ChangeRequestFormStakeHoldersSerializer,
                partial,
                self,
                form_code,
                batchno,
                enums.CREnum.SUBMIT.value
            )

        change_request.form_add_edit_delete(
                form_data['frm_attachments'],
                models.ChangeRequestFormAttachments,
                enums.CREntitiesEnum.CR_FRM_ATTACHMENT.value,
                serializers.ChangeRequestFormAttachmentsSerializer,
                partial,
                self,
                form_code,
                batchno,
                enums.CREnum.SUBMIT.value
            )

        change_request.form_add_edit_delete(
                form_data['frm_details'],
                models.ChangeRequestFormDetails,
                enums.CREntitiesEnum.CR_FRM_DETAIL.value,
                serializers.ChangeRequestFormDetailsSerializer,
                partial,
                self,
                form_code,
                batchno,
                enums.CREnum.SUBMIT.value
            )
        
        serializer = self.get_serializer(instance)

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

        return Response(message, status=status.HTTP_200_OK)

class ChangeRequestFormApproversViewset(meviewsets.ModelViewSet):

    queryset = models.ChangeRequestFormApprovers.objects.all()
    serializer_class = serializers.ChangeRequestFormApproversSerializer
    pagination_class = paginators.SimplePageNumberPagination
    lookup_field = 'code'
    
    def list(self, request, *args, **kwargs):
        self.queryset = QuerySetHelper.Sort(self)
        return super(ChangeRequestFormApproversViewset, self).list(request)


class ChangeRequestFormAttachmentsViewset(meviewsets.ModelViewSet):
    queryset = models.ChangeRequestFormAttachments.objects.all()
    serializer_class = serializers.ChangeRequestFormAttachmentsSerializer
    pagination_class = paginators.SimplePageNumberPagination

    def list(self, request, *args, **kwargs):
        self.queryset = QuerySetHelper.Sort(self)
        self.queryset = QuerySetHelper.Filter(self)
        return super(
            ChangeRequestFormAttachmentsViewset, self
        ).list(request)


class ChangeRequestFormStakeHoldersViewset(meviewsets.ModelViewSet):

    queryset = models.ChangeRequestFormStakeHolders.objects.all()
    serializer_class = serializers.ChangeRequestFormStakeHoldersSerializer
    pagination_class = paginators.SimplePageNumberPagination

    def list(self, request, *args, **kwargs):
        self.queryset = QuerySetHelper.Sort(self)
        self.queryset = QuerySetHelper.Filter(self)
        return super(ChangeRequestFormStakeHoldersViewset, self).list(request)


class ChangeRequestFormDetailsViewset(meviewsets.ModelViewSet):

    queryset = models.ChangeRequestFormDetails.objects.all()
    serializer_class = serializers.ChangeRequestFormDetailsSerializer
    pagination_class = paginators.SimplePageNumberPagination

    def list(self, request, *args, **kwargs):
        self.queryset = QuerySetHelper.Sort(self)
        self.queryset = QuerySetHelper.Filter(self)
        return super(ChangeRequestFormDetailsViewset, self).list(request)


class ChangeRequestFormPost(APIView):

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

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

            form_header_data = {
                'requested_to_template_name': form_header['requested_to_template_name'],
                'requested_to_template_id': form_header['requested_to_template_id'],
                'requested_to_objective': form_header['requested_to_objective'],
                'requested_to_target_date': form_header['requested_to_target_date'],
                'requested_to_priority': form_header['requested_to_priority'],
                'description': form_header['description'],
                'status': form_header['status'],
                'company_desc': form_header['company_desc'],
                'department_desc': form_header['department_desc'],
                'requested_desc': form_header['requested_desc'],
                'requested_by_department': form_header['requested_by_department'],
                'requested_by_user': form_header['requested_by_user'],
                'requested_to_company': form_header['requested_to_company'],
                'requested_to_department': form_header['requested_to_department'],
                'requested_to_user': form_header['requested_to_user'],
                'template_no': form_header['template_no']
            }

            sp1 = transaction.savepoint()  # nothing will save to db

            serializer = serializers.ChangeRequestFormHeaderSerializer(
                data=form_header_data)
            
            if serializer.is_valid(raise_exception=True):
                serializer.save()
            
            frm_id = serializer.data['form_code']

            # create template approvers
            frm_approvers = form_header['frm_approvers']
            counter = 0
            for frm_approver in frm_approvers:
                frm_approver['form_code'] = frm_id
                counter = counter + 1
                data_list_approver.append(frm_approver)

            #append vendor and requestor in routing table
            counter = counter + 1
            vendor_data = {
                'level': counter,
                'delegation': 'Vendor/Implementor',
                'user': form_header['requested_to_user'],
                'form_code': frm_id
            }

            data_list_approver.append(vendor_data)
            counter = counter + 1
            requestor_data = {
                'level': counter,
                'delegation': 'Requestor',
                'user': form_header['requested_by_user'],
                'form_code': frm_id
            }

            data_list_approver.append(requestor_data)

            serializerApprover = serializers.ChangeRequestFormApproversSerializer(
                data=data_list_approver, many=True)

            if serializerApprover.is_valid(raise_exception=True):
                serializerApprover.save()
            
            # create template stakes
            if form_header['frm_stakes']:
                frm_stakes = form_header['frm_stakes']
                for frm_stake in frm_stakes:
                    frm_stake['form_code'] = frm_id
                    data_list_stake.append(frm_stake)

                serializerStake = serializers.ChangeRequestFormStakeHoldersSerializer(
                    data=data_list_stake, many=True)

                if serializerStake.is_valid(raise_exception=True):
                    serializerStake.save()
            
            # create template attachments
            if form_header['frm_attachments']:
                frm_attachments = form_header['frm_attachments']
                for frm_attachment in frm_attachments:
                    frm_attachment['form_code'] = frm_id
                    data_list_attach.append(frm_attachment)

                serializerAttach = serializers.ChangeRequestFormAttachmentsSerializer(
                    data=data_list_attach, many=True)

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

            # create template details
            if form_header['frm_details']:
                frm_details = form_header['frm_details']
                for frm_detail in frm_details:
                    frm_detail['form_code'] = frm_id
                    data_list_detail.append(frm_detail)

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

            # if status is pending send initial email and
            # generate template id
            if serializer.data['status'].lower() == 'pending':
                template_no = serializer.data['template_no']
                CR_Prefix = serializer.data['requested_to_template_id']
                
                # generate batchno history
                batchno = get_max_batchno("batch")

                # generate requested template id
                requested_to_template_id = generate_template_id(
                    CR_Prefix, frm_id, template_no
                )

                #save to history for request template id tracking
                crhistory_create_save(
                    batchno,
                    enums.CREnum.CREATE.value,
                    enums.CREnum.ADD.value,
                    enums.CREntitiesEnum.CR_FRM_HEADER.value,
                    frm_id,
                    '',
                    serializer.data,
                    requested_to_template_id,
                    template_no)
                
                min_level = models.ChangeRequestFormApprovers.objects.filter(
                    form_code=frm_id
                ).aggregate(Min('level'))
                
                min_level = min_level.get('level__min')
                
                next_approver_email(frm_id, min_level)
            
            message = {
                'code': 201,
                'status': 'success',
                'message': 'Form 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': 'Request was not able to process' + str(e),
            }
            return Response(message,
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)