"""Ce fichier contient des fonctions qui permettent de convertir des données complexes,
 telles que des querysets et des instances de modèle, en types de données Python qui peuvent ensuite
 être facilement rendus en JSON, XML ou d'autres types de contenu.
"""

from rest_framework import serializers
from typing import Optional, Tuple


from .. import constants
from ..models import (Competence, Decision, Garnison, Marque, MarquesGroupe,
                      Poste, PAM, Administres_Pams, FormationEmploi, Postes_Pams,
                      DecisionChoices, DecisionTree, FMOB)

from .commun import AssignmentState
from ..utils.attributes import safe_rgetattr
from ..utils.decisions import KEY_CREATE, KEY_UPDATE, get_available_decisions
from ..utils.permissions import (KEY_READ, KEY_WRITE, Profiles,
                                 get_profiles_by_adm)
from .commun import ChoicesSerializer
from .filiere import FiliereSerializer
from .fonction import FonctionSerializer
from .formation_emploi import FormationEmploiSerializer
from .sous_vivier import SousVivierSerializer
from .fmob import FmobSerializer
from .administre import AdministreSerializer


CTX_KEY_DECISIONS = 'decisions'
CTX_KEY_PROFILES = 'profiles'


class ScoringValidator(serializers.Serializer):
    """ pour interprêter et valider le contenu JSON """
    sous_vivier_id = serializers.CharField(max_length=100, required=True, allow_null=False, allow_blank=False)
    pam_id = serializers.CharField(max_length=100, required=True, allow_null=False, allow_blank=False)

class ScoringSelectifValidator(serializers.Serializer):
    """ pour interprêter et valider le contenu JSON """
    sous_vivier_id = serializers.CharField(max_length=100, required=True, allow_null=False, allow_blank=False)
    administre_id = serializers.ListField()
    poste_id = serializers.ListField()
    pam_id = serializers.CharField(max_length=100)


class SimpleDecisionPosteSerializer(serializers.ModelSerializer):
    """Cette classe sera responsable de la conversion des objets decision en type json contenant uniquement les informations mentionnées dans la variable fields.
    Si fields='__all__', alors toutes les variables liées à cette classe seront affichées dans le json.
    """
    grade = serializers.ReadOnlyField(source='administre.grade.gr_code')
    nom = serializers.ReadOnlyField(source='administre.a_nom')
    prenom = serializers.ReadOnlyField(source='administre.a_prenom')

    class Meta:
        model = Decision
        fields = '__all__'

class AdministresPamsSerializer(serializers.ModelSerializer):

    class DecisionLocalSerializer(serializers.ModelSerializer):
        """Classe de représentation locale d'une décision"""

        class PosteLocalSerializer(serializers.Serializer):
            """Classe de représentation locale d'un poste"""

            class FormationEmploiLocalSerializer(FormationEmploiSerializer):
                """Classe de représentation locale d'une FE"""

                class Meta:
                    model = FormationEmploi
                    fields = ['fe_code', 'fe_libelle', 'fe_mere_credo', 'fe_mere_la', 'fe_garnison_lieu', 'mere']

            p_id = serializers.ReadOnlyField()
            formation_emploi = FormationEmploiLocalSerializer(read_only=True)

            class Meta:
                model = Poste
                fields = '__all__'

        poste = PosteLocalSerializer(read_only=True)

        class Meta:
            model = Decision
            fields = '__all__'

    administre = AdministreSerializer()
    pam_id = serializers.ReadOnlyField(source='pam.pam_id')
    pam_date = serializers.ReadOnlyField(source='pam.pam_date')
    pam_libelle = serializers.ReadOnlyField(source='pam.pam_libelle')
    pam_statut = serializers.ReadOnlyField(source='pam.pam_statut')
    fmobs = FmobSerializer()
    decision = DecisionLocalSerializer()
    profils = serializers.SerializerMethodField()
    decisions_possibles = serializers.SerializerMethodField()
    etat_fe_bmob = serializers.SerializerMethodField()

    def get_decisions_possibles(self, obj: Administres_Pams) -> Tuple[DecisionChoices]:
        decisions_by_adm = self.context.get(CTX_KEY_DECISIONS) or get_available_decisions((obj,), user=self.context['request'].user)
        decisions = decisions_by_adm.get(obj.id) or {}
        return {'creation': decisions.get(KEY_CREATE) or (), 'maj': decisions.get(KEY_UPDATE) or ()}

    def get_profils(self, obj: Administres_Pams) -> Tuple[Profiles]:
        profiles_by_adm = self.context.get(CTX_KEY_PROFILES) or get_profiles_by_adm(self.context['request'].user, obj)
        profiles = profiles_by_adm.get(obj.id) or {}
        return {'lecture': profiles.get(KEY_READ) or (), 'ecriture': profiles.get(KEY_WRITE) or ()}

    def get_etat_fe_bmob(self, obj: Administres_Pams) -> Optional[AssignmentState]:
        decision = safe_rgetattr(obj, f'{Administres_Pams.Cols.REL_DECISION}.{Decision.Cols.STATUT}')
        if not decision or DecisionTree.ME not in DecisionChoices(decision).trees:
            return None

        profiles_by_adm = self.context.get(CTX_KEY_PROFILES) or get_profiles_by_adm(self.context['request'].user, obj)
        profiles = (profiles_by_adm.get(obj.id) or {}).get(KEY_READ) or ()
        if Profiles.PCP in profiles:
            return AssignmentState.RESTANT

        current = Profiles.PCP_ACTUEL in profiles
        future = Profiles.PCP_FUTUR in profiles
        if current and future:
            return AssignmentState.ENTRANT_SORTANT
        if current:
            return AssignmentState.SORTANT
        if future:
            return AssignmentState.ENTRANT
        return None

    def to_representation(self, instance):
        ret = super().to_representation(instance)

        ret.update(ret.pop('administre'))
        ret['a_statut_pam'] = ret.pop('a_statut_pam_annee')
        
        if 'fmobs' in ret and ret['fmobs']:
            ret['fmobs'] = [ret['fmobs']]

        return ret
    class Meta:
        model = Administres_Pams
        fields = '__all__'

class PAMSerializer(serializers.ModelSerializer):
    """Cette classe sera responsable de la conversion des objets decision en type json contenant uniquement les informations mentionnées dans la variable fields.
    Si fields='__all__', alors toutes les variables liées à cette classe seront affichées dans le json.
    """
    class Meta:
        model = PAM
        fields = '__all__'


class MarquesGroupeSerializer(serializers.ModelSerializer):
    """Cette classe sera responsable de la conversion des objets marquegroupes en type json contenant uniquement les informations mentionnées dans la variable fields.
    Si fields='__all__', alors toutes les variables liées à cette classe seront affichées dans le json.
    """
    class Meta:
        model = MarquesGroupe
        fields = '__all__'


class MarqueSerializer(serializers.ModelSerializer):
    """Cette classe sera responsable de la conversion des objets marque en type json contenant les informations de marque et les champs de marquesgroupe liés à chaque marque.
    Si fields='__all__', alors toutes les variables liées à cette classe seront affichées dans le json.
    """
    groupe_marques = MarquesGroupeSerializer()

    class Meta:
        model = Marque
        fields = '__all__'


class GarnisonSerializer(serializers.ModelSerializer):
    """Cette classe sera responsable de la conversion des objets garnisons en type json contenant les informations mentionnées dans la variable fields.
    Si fields='__all__', alors toutes les variables liées à cette classe seront affichées dans le json.
    """
    class Meta:
        model = Garnison
        fields = '__all__'


class SousVivierAssociationSerializer(serializers.ModelSerializer):
    """Cette classe sera responsable de la creation d'un json contenant les champs du SousVivier
    et les champs de filière liés à chaque SousVivier.
    Si fields='__all__', alors toutes les variables liées à cette classe seront affichées dans le json.
    """
    filiere = FiliereSerializer()
    sous_vivier = SousVivierSerializer()

    class Meta:
        fields = '__all__'

class CompetenceSerializer(serializers.ModelSerializer):
    """Cette classe sera responsable de la conversion des objets competences en type json contenant les informations mentionnées dans la variable fields.
    Si fields='__all__', alors toutes les variables liées à cette classe seront affichées dans le json.
    """
    class Meta:
        model = Competence
        fields = '__all__'


class PosteSerializer(serializers.ModelSerializer):
    """Cette classe sera responsable de la conversion des objets postes en type json contenant les champs du poste
    et les champs de fonction, formation_emploi, sous_vivier et decisions liés à chaque poste. Cette classe va également ordonner le json par p_id et valider certaines variables.
    Si fields='__all__', alors toutes les variables liées à cette classe seront affichées dans le json.
    """
    p_id = serializers.ReadOnlyField()
    fonction = FonctionSerializer()
    formation_emploi = FormationEmploiSerializer()
    sous_viviers = SousVivierSerializer(many=True)
    p_nb_prepositionne = serializers.IntegerField(read_only=True)
    p_nb_positionne = serializers.IntegerField(read_only=True)
    p_nb_omi_en_cours = serializers.IntegerField(read_only=True)
    p_nb_omi_active = serializers.IntegerField(read_only=True)
    p_poids_competences = serializers.IntegerField(read_only=True)
    p_poids_filiere = serializers.IntegerField(read_only=True)
    p_poids_nf = serializers.IntegerField(read_only=True)
    
    class Meta:
        model = Poste
        ordering = ['p_id']
        fields = '__all__'


class PostesPamsSerializer(serializers.ModelSerializer):

    pam_id = serializers.ReadOnlyField(source='p_pam.pam_id')
    pam_date = serializers.ReadOnlyField(source='p_pam.pam_date')
    pam_libelle = serializers.ReadOnlyField(source='p_pam.pam_libelle')
    pam_statut = serializers.ReadOnlyField(source='p_pam.pam_statut')
    decisions = SimpleDecisionPosteSerializer()
    poste = PosteSerializer()
   
    
    def to_representation(self, instance):
        ret = super().to_representation(instance)
        ret.update(ret.pop('poste'))

        if ret['decisions']:
            ret['decisions'] = [ret['decisions']]
        else :
            ret['decisions'] = []



        return ret

    class Meta:
        model = Postes_Pams
        fields = '__all__'



class RefStatutPamChoicesSerializer(ChoicesSerializer):
    """ Représente un élément StatutPamChoices dans les données de référence """

    inclusAlgo = serializers.ReadOnlyField(source='calc_enabled')
    affectationManuellePossible = serializers.ReadOnlyField(source='dec_enabled')


class RefAvisPosteChoicesSerializer(RefStatutPamChoicesSerializer):
    """ Représente un élément AvisPosteChoices dans les données de référence """
