from django.db.transaction import atomic
from django.shortcuts import get_object_or_404
from django.utils import timezone
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
import datetime
from ..models import Administre, Decision, DecisionChoices, Notation, Poste
from ..serializers.decision import CreateDecisionSerializer, DecisionSerializer
from ..utils.decorators import class_logger
from .commun import (GestionnairePermission, api_exception,
                     execution_time_viewset, query_count_viewset)


@class_logger
@execution_time_viewset
@query_count_viewset
class DecisionView(ModelViewSet):
    """
    Cette classe est dédiée au vue des decisions.
    """
    permission_classes = [IsAuthenticated, GestionnairePermission]
    serializer_class = DecisionSerializer
    queryset = Decision.objects.all()

    def get_serializer_class(self):
        """ Renvoie un serializer particulier pour la création """
        if self.action == 'create':
            return CreateDecisionSerializer
        return self.serializer_class

    def list(self, request: Request, pk=None) -> Response:
        """Cette fonction envoie les informations du poste lié à l'administre dans une décision avec le score de l'administre et inversement.
        TODO: rajouter la possibilité d'envoyer les informations du poste lié à l'administré selon l'année du PAM dans lequel on se situe

        :type request: rest_framework.request.Request
        :param request: Request contenant l'administre ou le poste.

        :return:    - **res_decision** (*Response*): Json contenant le classement.
        """
        decision = []
        q = 'poste'
        administre_id = None
        poste_id = None

        if 'pam_annee' in  request.query_params:
            annee_pam = request.query_params['pam_annee']
        else :
            annee_pam = None

        if 'administre_id' in request.query_params:
            administre_id = request.query_params['administre_id']
            administre_pam_id = str(administre_id) + str(annee_pam)
            administres_keys = (
                'poste__p_id', 'poste__p_nf', 'poste__p_domaine', 'poste__p_filiere',
                'poste__p_eip', #'poste__p_avis', TODO : Change p_avis to postesPAM
                'poste__formation_emploi__fe_code', 'poste__competences',
                'poste__p_notes_gestionnaire', 'poste__p_liste_id_marques',
                'poste__formation_emploi__fe_libelle',
                'poste__formation_emploi__fe_garnison_lieu',
                'poste__p_dep', 'poste__p_code_fonction',
                'poste__p_fonction',
                'de_decision', 'de_date_decision', 'de_notes_gestionnaire')
            decision = Decision.objects.filter(administre_pam_id = administre_pam_id).select_related('poste')
            decision = list(decision.values(*administres_keys))

        if 'poste_id' in request.query_params:
            q = "administre"
            poste_id = request.query_params['poste_id']
            poste_pam_id = poste_id + str(annee_pam)
            postes_keys = (
                'administre__a_id_sap', 'administre__a_nom', 'administre__a_prenom', 'administre__administre__a_statut_pam_annee',
                'administre__a_fonction', 'administre__a_code_fonction', 'administre__a_liste_id_competences',
                'administre__grade_id', 'administre__a_liste_id_marques', 'de_decision', 'de_date_decision',
                'de_notes_gestionnaire', 'administre__a_notes_gestionnaire', 'administre__a_liste_id_competences',
                'administre__decision__poste_id')

            decision = Decision.objects.filter(poste_pam_id = poste_pam_id).select_related('administre')

            decision = list(decision.values(*postes_keys))
        res_decision = []
        if len(decision) > 0:
            for k in range(len(decision)):
                decision_unit = decision[k]
                try:
                    administre_id = decision_unit.administre_id
                except:
                    administre_id = None

                try:
                    poste_id = decision_unit.poste_id
                except:
                    poste_id = None

                try:
                    res_decision_unit = {q: {}, 'no_score_administre': Notation.objects.get(
                        administre_id=administre_id,
                        poste_id=poste_id, pam_id = annee_pam).no_score_administre}
                except:
                    res_decision_unit = {q: {}, 'no_score_administre': None}
                for key in decision_unit:
                    if (q + "__") in key:
                        res_decision_unit[q][key.replace(q + '__', '')] = decision_unit[key]
                    else:
                        res_decision_unit[key] = decision_unit[key]
                # Ajout du relevé des décisions sur le poste (cas q = "poste")
                if q == "poste":
                    res_decision_unit[q]['p_nb_prepositionne'] = Decision.objects.filter(
                        de_decision=DecisionChoices.PREPOSITIONNE).count()
                    res_decision_unit[q]['p_nb_positionne'] = Decision.objects.filter(
                        de_decision=DecisionChoices.POSITIONNE).count()
                    res_decision_unit[q]['p_nb_omi_active'] = Decision.objects.filter(
                        de_decision=DecisionChoices.OMI_ACTIVE).count()
                    res_decision_unit[q]['p_nb_omi_en_cours'] = Decision.objects.filter(
                        de_decision=DecisionChoices.OMI_EN_COURS).count()
                res_decision.append(res_decision_unit)
        return Response(res_decision)

    @atomic
    def perform_create(self, serializer) -> None:
        """Cette fonction crée une decision à partir de données validées """

        data = serializer.validated_data
        annee_pam = self.request.query_params['pam__in']
        administre_id = data.get('administre_id')
        administre_id_pam = str(administre_id) + annee_pam
        poste_id = data.get('poste_id')
        poste_id_pam = poste_id + annee_pam
        de_decision = data.get('de_decision')
        delete_former = data.get('delete_former')
        a = get_object_or_404(Administre, pk=administre_id)
        p = get_object_or_404(Poste, pk=poste_id)
        self.check_object_permissions(self.request, a)
        self.check_object_permissions(self.request, p)

        qs_former = Decision.objects.filter(pk=administre_id)
        if delete_former:
            qs_former.delete()
        elif qs_former.exists():
            raise api_exception(status.HTTP_400_BAD_REQUEST, "une décision existe déjà pour cet administré")

        Decision.objects.create(administre_id=administre_id, poste_id=poste_id, de_decision=de_decision, de_date_decision=timezone.now(),
                                administre_pam_id=administre_id_pam, poste_pam_id=poste_id_pam )

    def perform_update(self, serializer) -> None:
        """ Met à jour la décision à partir de données validées. La date est mise à jour en même temps que le statut """
        data = serializer.validated_data
        if hasattr(data, Decision.Cols.STATUT):
            setattr(data, Decision.Cols.DATE, timezone.now())
        super().perform_update(serializer)
