from rest_framework import viewsets as meviewsets
from rest_framework.views import APIView
from app.applicationlayer.cms.template import serializers

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

class UserList(APIView):

    pagination_class = CustomPagination

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

        try:
            serializer = ChangeRequestList
            dept = self.request.query_params['department_code']
            company = self.request.query_params['company_code']
            user_list = models.AllowedCompany.objects.filter(
                Q(company_pivot=company) &
                Q(group_pivots=dept)
            )
            user_list = user_list.exclude(id=1).values('id_number')
            queryset = models.User.objects.filter(code__in=user_list).order_by('name')

            page = self.paginate_queryset(queryset)

            if page is not None:

                serializer = ChangeRequestList(page, many=True)

                message = status_message_response(
                    200,
                    'success',
                    'list of User found',
                    serializer.data
                )
                return self.get_paginated_response(message)
        except Exception as e:
            return Response(
                {"message": "this endpoint expect a query params department_id"},
                status=status.HTTP_400_BAD_REQUEST
            )

    @property
    def paginator(self):
        """
        The paginator instance associated with the view, or `None`.
        """
        if not hasattr(self, '_paginator'):
            if self.pagination_class is None:
                self._paginator = None
            else:
                self._paginator = self.pagination_class()
        return self._paginator

    def paginate_queryset(self, queryset):
        """
        Return a single page of results, or `None` if pagination is disabled.
        """
        if self.paginator is None:
            return None
        return self.paginator.paginate_queryset(queryset, self.request, view=self)

    def get_paginated_response(self, data):
        """
        Return a paginated style `Response` object for the given output data.
        """
        assert self.paginator is not None
        return self.paginator.get_paginated_response(data)


class ChangeRequestTemplatesViewset(meviewsets.ModelViewSet):

    queryset = models.ChangeRequestTemplateHeader.objects.all()
    lookup_field = 'template_no'
    serializer_class = serializers.ChangeRequestTemplatesSerializer
    pagination_class = CustomPagination
    
    def list(self, request, *args, **kwargs):
        
        self.serializer_class = serializers.ChangeRequestTemplatesSerializerList

        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 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.queryset.filter(
            template_no=str(self.kwargs.get('template_no'))
        ).first()

        serializer = self.get_serializer(instance)
        return Response(serializer.data)

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

            template_no = self.kwargs['template_no']

            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)
        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,
                serializers.ChangeRequestTemplateAttachmentsSerializer,
                partial,
                self,
                template_no
            )

            tmp_add_edit_delete(
                body_data['tmp_details'],
                models.ChangeRequestTemplateDetails,
                enums.LogEntitiesEnum.ChangeRequestTemplateDetails.value,
                serializers.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)

        return Response(
            "Unable to edit due to existing transaction",
                status=status.HTTP_400_BAD_REQUEST
        )


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

            serializer = serializers.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': 'Request was not able to process' + str(e),
            }
            return Response(message,
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)
