from django.shortcuts import render
from app.entities import models
from app.applicationlayer import paginators

from datetime import datetime

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

from app.applicationlayer.cms.utils_cr import (
    number_generator,
    crhistory_save,
    entity_log_bulk,
    crhistory_log_bulk_delete,
    crhistory_log_bulk_delete_masterattach,
    send_mail_requestor,
    next_approver_email,
    send_mail_vendor,
    crhistory_create_save,
    reminder_trigger_save,
    overdue_trigger_save,
    reset_autoemail_tables,
    cancelled_user_trigger,
    send_mail_approver
)
from app.applicationlayer.utils import model_to_dict
from app.entities import enums, models
from app.applicationlayer.utils import log_save, CustomPagination
from app.applicationlayer.cms.template import serializers
from django.db.models import Min
from django.conf import settings

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
REVISED_MESSAGE = settings.REVISED_MESSAGE
CANCEL_MESSAGE = settings.CANCEL_MESSAGE
APPROVER_REJECT_MESSAGE = settings.APPROVER_REJECT_MESSAGE


def list_by_user(user_id_number):
    try:

        priviledgeCrs = models.AllowedCompany.objects.filter(
            Q(id_number=user_id_number) &
            Q(view_all_change_request=True)
        ).values('group_pivots')
        
        return_queryset = models.ChangeRequestFormHeader.objects.filter(
            (Q(frm_approvers__user__code=user_id_number) |
            Q(frm_stakes__user__code=user_id_number) |
            Q(requested_by_user=user_id_number) |
            Q(requested_to_department__in=priviledgeCrs)) &
            Q(is_active=True)
        ).exclude(
                Q(status__icontains='Draft') &
                ~Q(requested_by_user=user_id_number)
        ).distinct()

        return return_queryset

    except Exception as e:
        
        return_queryset = None

        return return_queryset

def list_by_user_archived(user_id_number):
    try:

        priviledgeCrs = models.AllowedCompany.objects.filter(
            Q(id_number=user_id_number) &
            Q(view_all_change_request=True)
        ).values('group_pivots')
        
        return_queryset = models.ChangeRequestFormHeader.objects.filter(
            (Q(frm_approvers__user__code=user_id_number) |
            Q(frm_stakes__user__code=user_id_number) |
            Q(requested_by_user=user_id_number) |
            Q(requested_to_department__in=priviledgeCrs)) &
            Q(is_active=False)
        ).exclude(
                Q(status__icontains='Draft') &
                ~Q(requested_by_user=user_id_number)
        ).distinct()

        return return_queryset

    except Exception as e:
        
        return_queryset = None

        return return_queryset

def list_by_user_without_dept(user_id_number):
    try:
        return_queryset = models.ChangeRequestFormHeader.objects.filter(
            (Q(frm_approvers__user__code=user_id_number) |
            Q(frm_stakes__user__code=user_id_number) |
            Q(requested_by_user=user_id_number)) &
            Q(is_active=True)
        ).exclude(
                Q(status__icontains='Draft') &
                ~Q(requested_by_user=user_id_number)
        ).distinct()
        
        return return_queryset

    except Exception as e:
        
        return_queryset = None

    return return_queryset


def filter_base(base_queryset,
                company_requested_to,
                department_requested_to,
                date_modified_from,
                date_modified_to,
                date_required_from,
                date_required_to,
                form_type):

    return_queryset = base_queryset
    
    try:

        if company_requested_to:
            return_queryset = return_queryset.filter(
                requested_to_company__exact=company_requested_to)

        if department_requested_to:
            return_queryset = return_queryset.filter(
                requested_to_department__exact=department_requested_to)

        if form_type == 'open':
            return_queryset = return_queryset.filter(~Q(status='Completed & Accepted'))
        elif form_type == 'closed':
            return_queryset = return_queryset.filter(status='Completed & Accepted')

        date_modified = []
        #comment
        if date_modified_from and date_modified_to:        
            for query in return_queryset:
                created = datetime.strftime(query.created, "%Y-%m-%d")
                
                if created >= date_modified_from and created <= date_modified_to:
                    date_modified.append(query.id)

            return_queryset = return_queryset.filter(id__in=date_modified)
            
        date_required = []
        
        if date_required_from and date_required_to:
            for query in return_queryset:
                if query.requested_to_target_date:
                    requested_to_target_date = datetime.strftime(
                        query.requested_to_target_date,
                        "%Y-%m-%d")
                    
                    if requested_to_target_date >= date_required_from and requested_to_target_date <= date_required_to:
                        date_required.append(query.id)

            return_queryset = return_queryset.filter(
                id__in=date_required
            )
                
        return_queryset

    except Exception as e:
        pass

    return return_queryset


def filter_overdue(base_queryset):

    return_queryset = base_queryset

    try:
        exclude_status = ['Cancelled', 'Completed & Accepted', 'Closed', 'Draft']
        now = datetime.now()
        overdue = []
        
        for query in return_queryset:
            if (query.requested_to_target_date < now):
                overdue.append(query.form_code)
            
        return_queryset = return_queryset.filter(
            form_code__in=overdue
        ).exclude(status__in=exclude_status)

    except Exception as e:
        pass

    return return_queryset


def filter_status(base_queryset,
                  status):

    return_queryset = base_queryset    
    try:
        if status:
            if status.lower() == 'completed_accepted':
                status = 'Completed & Accepted'
            
            return_queryset = return_queryset.filter(
                status__iexact=status
            )

        return_queryset

    except Exception as e:
        pass

    return return_queryset


def filter_awaiting(base_queryset,
                    user_id_number):

    return_queryset = base_queryset
    
    try:
        awaiting_included = []
                
        for query in return_queryset:
            
            next_approvers = models.ChangeRequestFormApprovers.objects.filter(
                Q(form_code=query.form_code) &
                Q(is_action=True)
            ).order_by("level")
            
            for next_approver in next_approvers:
                if next_approver.user.code == user_id_number:
                    awaiting_included.append(query.form_code)
                    
        return_queryset = return_queryset.filter(
            form_code__in=awaiting_included
        ).exclude(
            (Q(status__icontains='Rejected') |
            Q(status__icontains='Completed & Accepted') |
            Q(status__icontains='Cancelled') |
            Q(status__icontains='Draft'))
        )
    
    except Exception as e:
        pass

    return return_queryset


def form_add_edit_delete(form_request_body,
                         queryset,
                         entity,
                         serializer_data,
                         partial,
                         self,
                         form_code,
                         batch_no,
                         main_action):
    
    # delete ids not in request body
    request_ids = [i['id'] for i in form_request_body if "id" in i]

    delete_query = queryset.objects.filter(
        form_code=form_code
    ).exclude(
        id__in=request_ids
    )

    if delete_query.count() > 0:
        crhistory_log_bulk_delete(delete_query,
                                  entity,
                                  queryset,
                                  form_code,
                                  batch_no,
                                  main_action
        )

    # update or create
    for i in form_request_body:
        if "id" in i:
            frm_instance = queryset.objects.get(
                pk=i['id']
            )

            frm_code = {
                "form_code": form_code
            }
            data = {**i, **frm_code}

            serializer = serializer_data(frm_instance,
                                         data=data,
                                         partial=partial,
                                         context={"request":self.request})

            serializer.is_valid(raise_exception=True)
            old_instance = model_to_dict(frm_instance)
            self.perform_update(serializer)
            new_instance = serializer.data
            # comment
            crhistory_save(
                batch_no,
                main_action,
                enums.CREnum.UPDATE.value,
                entity,
                form_code,
                old_instance,
                new_instance
            )

        else:
            frm_code = {
                "form_code": form_code
            }
            data = {**i, **frm_code}
            serializer = serializer_data(data=data,
                                         context={"request":self.request})
            serializer.is_valid(raise_exception=True)

            self.perform_create(serializer)
            new_instance = serializer.data
            
            crhistory_save(
                batch_no,
                main_action,
                enums.CREnum.ADD.value,
                entity,
                form_code,
                None,
                new_instance
            )

    return True


def attachment_add_edit_delete(form_request_body,
                               queryset,
                               entity,
                               serializer_data,
                               partial,
                               self,
                               form_code,
                               batch_no,
                               main_action,
                               id_number):
    
    # delete ids not in request body
    request_ids = [i['id'] for i in form_request_body if "id" in i]

    delete_query = queryset.objects.filter(
        form_code=form_code
    ).exclude(
        id__in=request_ids
    )
    
    if delete_query.count() > 0:
        crhistory_log_bulk_delete_masterattach(delete_query,
                                               entity,
                                               queryset,
                                               form_code,
                                               batch_no,
                                               main_action
        )

    # update or create
    for i in form_request_body:
        if "id" in i:
            frm_instance = queryset.objects.get(
                pk=i['id']
            )

            data_old = {
                'id': frm_instance.id,
                'attachment_type': frm_instance.attachment_type,
                'attachment_name': frm_instance.attachment_name,
                'file_name': frm_instance.file_name,
                'description': frm_instance.description,
                'file_upload': frm_instance.file_upload.id
            }

            data_new = {
                'id': i['id'],
                'attachment_type': i['attachment_type'],
                'attachment_name': i['attachment_name'],
                'file_name': i['file_name'],
                'description': i['description'],
                'file_upload': i['file_upload']
            }
            
            frm_code = {
                "form_code": form_code,
                "uploaded_by": id_number
            }
            data = {**i, **frm_code}
            
            old_instance = model_to_dict(frm_instance)
            
            if not data_old == data_new:
                serializer = serializer_data(frm_instance,
                                data=data,
                                partial=partial,
                                context={"request": self.request})

                serializer.is_valid(raise_exception=True)

                self.perform_update(serializer)
                new_instance = serializer.data
                
                crhistory_save(
                    batch_no,
                    main_action,
                    enums.CREnum.UPDATE.value,
                    entity,
                    form_code,
                    old_instance,
                    new_instance
                )

        else:
            frm_code = {
                "form_code": form_code,
                "uploaded_by": id_number
            }
            data = {**i, **frm_code}
            serializer = serializer_data(data=data,
                                         context={"request":self.request})
            serializer.is_valid(raise_exception=True)

            self.perform_create(serializer)
            new_instance = serializer.data
            
            crhistory_save(
                batch_no,
                main_action,
                enums.CREnum.ADD.value,
                entity,
                form_code,
                None,
                new_instance
            )

    return True


def check_vendor_level(position, routing_level, form_code):

    query = ''

    if position == 'next':

        query = models.ChangeRequestFormApprovers.objects.filter(
                    form_code=form_code,
                    level=int(routing_level),
                    delegation__name='Vendor/Implementor'
                ).first()
            
        return query
    
    elif position == 'last':

        query = models.ChangeRequestFormApprovers.objects.filter(
                    form_code=form_code,
                    delegation__name='Vendor/Implementor'
                ).order_by('level').last()
        
        return query

    elif position == 'prev':

        query = models.ChangeRequestFormApprovers.objects.filter(
                    Q(form_code=form_code) &
                    Q(action='Completed') &
                    Q(delegation__name='Vendor/Implementor') &
                    Q(level__lte=int(routing_level))
                )
        
        return query


def update_form_approver(form_code, level, apprv_type, date_now):

    if apprv_type == 'next':
        # update next approver details
        models.ChangeRequestFormApprovers.objects.filter(
            Q(form_code=form_code) & Q(level=int(level))
            ).update(
                date_sent=date_now,
                is_action=True
            )

    elif apprv_type == 'current':

        # update current approver details
        models.ChangeRequestFormApprovers.objects.filter(
            Q(form_code=form_code) & Q(level=int(level))
            ).update(
                is_action=False
            )


def update_form_header_status(form_code, status):
    models.ChangeRequestFormHeader.objects.filter(
        form_code=form_code
    ).update(status=status)

    return True

def cr_routing_actions(approver_instance, current_user, move_to_level):

    form_code = approver_instance['form_code']
    delegation = approver_instance['delegation']['name']
    action = approver_instance['action'].lower()
    level = approver_instance['level']
    next_level = int(approver_instance['level']) + 1
    remarks = approver_instance['remarks']
    
    if approver_instance['action'].lower() == 'approved':
        
        the_next_vendor = check_vendor_level(
            'next', next_level, form_code)

        if the_next_vendor:
            
            the_last_vendor = check_vendor_level(
                'last', level, form_code)
            
            # changed form status to Approved
            if the_next_vendor == the_last_vendor:
                update_form_header_status(form_code, 'Approved')
            
        # NOTIF MSG FOR REQUESTOR
        requestor_notification_msg = REQUESTOR_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)
        
        date_now = datetime.now()

        # update next approver details
        update_form_approver(form_code, next_level, 'next', date_now)
        
        # update current approver details
        update_form_approver(form_code, level, 'current', date_now)

        #save details for reminder trigger
        reminder_trigger_save(form_code, date_now, date_now)
                
    elif approver_instance['action'].lower() == 'rejected':

        # reject from requestor action
        if delegation.lower() == 'requestor':
            notification_msg = REVISED_MESSAGE.split(';')[0]
            
            # send reject email to vendor
            send_mail_vendor(
                current_user, form_code, delegation,
                notification_msg, action,
                remarks, level
            )

            prev_level = int(level) - 1
            # reset last vendor details
            models.ChangeRequestFormApprovers.objects.filter(
                Q(form_code=form_code) &
                Q(level=int(prev_level))
                ).update(
                    is_action=True,
                    action='Acknowledged',
                    action_date=datetime.now(),
                    date_sent=datetime.now()
            )
        
        else:
            
            back_to_approver = models.ChangeRequestFormApprovers.objects.filter(
                Q(form_code=form_code) &
                Q(level=move_to_level) &
                (~Q(action=None) | Q(delegation__name='Requestor'))
            ).first()
            
            if back_to_approver.delegation.name.lower() == '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
                )

                update_form_header_status(form_code, 'Rejected')

            elif back_to_approver.delegation.name.lower() == 'vendor/implementor':

                vendor_notification_msg = REVISED_MESSAGE.split(';')[0]

                # send reject email to vendor
                send_mail_vendor(
                    current_user, form_code, delegation,
                    vendor_notification_msg, action,
                    remarks, level
                )

                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
                )

                # reset selected vendor details
                models.ChangeRequestFormApprovers.objects.filter(
                    Q(form_code=form_code) &
                    Q(level=int(move_to_level))
                    ).update(
                        is_action=True,
                        action='Acknowledged',
                        action_date=datetime.now(),
                        date_sent=datetime.now()
                )
            
            else:
                
                approver_code = back_to_approver.user.code
                
                approver_notification_msg = APPROVER_REJECT_MESSAGE.split(';')[0]
                
                send_mail_approver(
                    current_user, form_code, delegation,
                    approver_notification_msg, action, remarks,
                    level, approver_code
                )

                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
                )

                # reset selected vendor details
                models.ChangeRequestFormApprovers.objects.filter(
                    Q(form_code=form_code) &
                    Q(level=int(move_to_level))
                    ).update(
                        is_action=True,
                        action=None,
                        action_date=None,
                        date_sent=datetime.now()
                )
        
        models.ChangeRequestFormApprovers.objects.filter(
            Q(form_code=form_code) & Q(level__gt=int(move_to_level))
            ).update(
                is_action=False,
                action=None,
                remarks=None,
                action_date=None,
                date_sent=None
        )

    elif approver_instance['action'].lower() == 'completed':

        the_last_vendor = check_vendor_level(
            'last', level, form_code)

        if (the_last_vendor and current_user == the_last_vendor.user.code and
            level == the_last_vendor.level):

            update_form_header_status(form_code, 'Approved')
        
        else:
            the_next_vendor = check_vendor_level(
                'next', next_level, form_code)
            
            if the_next_vendor:
                if the_next_vendor.user.code == the_last_vendor.user.code:
                    update_form_header_status(form_code, 'Approved')
            else:
                update_form_header_status(form_code, 'Pending')

            next_approver_email(form_code, next_level)

        date_now = datetime.now()

        # update next approver details
        update_form_approver(form_code, next_level, 'next', date_now)
        
        # update current approver details
        update_form_approver(form_code, level, 'current', date_now)

        # 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
        )
    
    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
        )

    elif action.lower() == 'accepted':
        
        update_form_header_status(form_code, 'Completed & 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
        )

        date_now = datetime.now()
        # update current approver details
        update_form_approver(form_code, level, 'current', date_now)

    elif action.lower() == 'cancelled':
        
        # send email and notif to anyone who received an email about the change request
        cancelled_user_trigger(form_code, action, level)
        
        # changed form status to cancelled        
        update_form_header_status(form_code, 'Cancelled')
        
        date_now = datetime.now()
        # update is_action for current level
        update_form_approver(form_code, level, 'current', date_now)
        
        reset_autoemail_tables(form_code)
        
    return True








        

            







