"""
Ce module contient les Utilitaires de scoring
"""
# import des pré requis
import json
from datetime import date
from django.db.models import Q
import time
import pandas as pd
import numpy as np
from django.utils import timezone
from .models import Administre, Domaine, FMOB, Fonction, FormationEmploi, Garnison, Grade, Poste, Notation, Postes_Pams, \
    PreferencesListe, Marque, MarquesGroupe, Filiere, Competence, StatutPamChoices, AvisPosteChoices as AvisPoste, \
    Administres_Pams
from .utils.logging import get_logger
import itertools

logger = get_logger(__name__)

# calcul de la date
today = date.today()


# Fonction de lancement du preprocessing et d'encoding
def encoding(a_fil, a_nf, a_comp, p_fil, p_nf, p_comp):
    """
    Retourne l'encodage de l' "eip + département" par rapport à un poste donné

    :type a_fil: numpy list
    :param a_fil: filière de l'eip actuel militaire

    :type a_nf: numpy list
    :param a_nf: nf de l'eip actuel militaire

    :type a_comp: ?
    :param a_comp: ?

    :type p_fil: numpy list
    :param p_fil: filière du poste vacant

    :type p_nf: numpy list
    :param p_nf: nf du poste vacant

    :type p_comp: ?
    :param p_comp: ?

    :return:    - **encoding** (*numpy list*): encoding de l'eip actuel du militaire et du département où il souhaite travailler par rapport à un poste vacant
    """
    b_fil = (a_fil == p_fil).astype(int)
    b_nf = (a_nf == p_nf).astype(int)

    b_comp = [len(list(filter(i.__contains__, j)))/len(j) if j!=[None] else 0 for i,j in zip(a_comp,p_comp)]
    logger.debug('Calcul des compétences')

    encoding = (b_fil, b_nf, b_comp)
    return encoding


# fonction de calcul du scores
def notePonderee(a_fil, a_nf, a_comp, p_fil, p_nf, p_comp, p_poids_fil, p_poids_nf, p_poids_comp):
    """
    Retourne la note d'une combinaison eip+département par rapport à un poste donné

    :type a_dom: numpy list
    :param a_dom: domaine de l'eip actuel du militaire

    :type a_fil: numpy list
    :param a_fil: filière de l'eip actuel militaire

    :type a_dep: numpy list
    :param a_dep: département où souhaite travailler le militaire

    :type a_nf: numpy list
    :param a_nf: nf de l'eip actuel militaire

    :type p_dom: numpy list
    :param p_dom: domaine du poste vacant

    :type p_fil: numpy list
    :param p_fil: filière du poste vacant

    :type p_dep: numpy list
    :param p_dep: département du poste vacant

    :type p_nf: numpy list
    :param p_nf: nf du poste vacant

    :type p_poids_dom: numpy list
    :param p_poids_dom: poids du domaine

    :type p_poids_fil: numpy list
    :param p_poids_fil: poids de la filière

    :type p_poids_dep: numpy list
    :param p_poids_dep: poids du département

    :type p_poids_nf: numpy list
    :param p_poids_nf: poids du nf


    :return:    - **note** (*float*): note de l'eip par rapport au poste à pourvoir

    """

    e = encoding(a_fil, a_nf, a_comp, p_fil, p_nf, p_comp)
    note = p_poids_fil * e[0] + p_poids_nf * e[1] + p_poids_comp * e[2]
    return note


def notations(pam_id, sv_id):
    """
    Remplit la colonne note de la table Notations

    :type sv_id: char
    :param sv_id: sous vivier étudié


    :return:    - **notations** (*dataframe*): table des notations avec les notes complétées


    """
    # Sélection des postes sur lesquels aucune décision n'est prise
    logger.debug('start scoring')

    colonnes_pos = ('id','poste_id', 'poste__competences', 'poste__p_filiere_id', 'poste__p_nf', 'p_avis_pam')
    colonnes_pos_name = ('poste_pam_id','poste_id', 'competences', 'p_filiere_id', 'p_nf', 'p_avis_pam')
    statut_poste =[AvisPoste.P1,AvisPoste.P2,AvisPoste.P3,AvisPoste.P4]

    postes = Postes_Pams.objects.values_list(*colonnes_pos).filter(Q(p_pam_id=pam_id) 
                                        & Q(poste__sous_viviers=sv_id) 
                                        & Q(decisions__de_decision__isnull=True)
                                        & Q(p_avis_pam__in=statut_poste))

    if not postes.exists:
        logger.debug(f"Pas de poste avec le bon statut {statut_poste} dans le sous vivier {sv_id} et le pam {pam_id}.")
        raise Exception("Aucun poste avec le bon statut.")

    pos = pd.DataFrame.from_records(postes, columns=colonnes_pos_name)
    pos=pos.groupby(['poste_pam_id','poste_id','p_filiere_id', 'p_nf', 'p_avis_pam'])['competences'].apply(list).reset_index(name='competences')


    # Remplire les poids des postes
    p_poids = ['p_poids_filiere', 'p_poids_nf', 'p_poids_competences']
    try:
        pos['p_poids'] = pos.apply(lambda x: Poste.objects.get(p_id=x['poste_id']).p_poids_filiere_nf_competences, axis=1)
        pos[p_poids] = pd.DataFrame(pos['p_poids'].tolist(), index=pos.index)

    except Exception as e:
        raise Exception("Tous les postes sont déjà affectés à un adminstré : Veuillez en sélectionner de nouveau")

    # Sélection des administrés dont le statut PAM est "à muter" et sur lesquels aucune décision n'est prise
    colonnes_adm = ('id','administre__a_id_sap', 'administre__a_filiere_futur_id',
                    'administre__a_nf_futur', 'administre__a_liste_id_competences')

    colonnes_adm_name = ('administre_pam_id', 'a_id_sap', 'a_filiere_futur_id',
                        'a_nf_futur', 'a_liste_id_competences')

    statut = [StatutPamChoices.A_MUTER,StatutPamChoices.A_ETUDIER]

    administres = Administres_Pams.objects.filter(pam_id=pam_id, administre__sous_vivier_id=sv_id, a_statut_pam_annee__in=statut,
                                            decision__de_decision__isnull=True).values_list(*colonnes_adm)

    if not administres.exists:
        logger.debug(f"Pas de poste avec le bon statut {statut} dans le sous vivier {sv_id} et le pam {pam_id}.")
        raise Exception(f"Aucun administrés avec le bon statut.")

    adm = pd.DataFrame.from_records(administres, columns=colonnes_adm_name)

    if len(adm) == 0:
        raise Exception("Arret du scoring 0 administrés dont le statut PAM est à muter et sur lesquels aucune décision n'est prise")

    logger.debug('Nb admin traités : %s', len(adm))
    logger.debug('Nb groupe_poste traités : %s', len(pos))

    adm=adm.groupby(['administre_pam_id','a_id_sap','a_filiere_futur_id','a_nf_futur'])['a_liste_id_competences'].apply(list).reset_index(name='a_liste_id_competences')

    
    # Ajout de la colonne Key avec la valeur 1 aux deux bases de données, pour pouvoir effectuer la jointure croisée.
    adm['key'] = 1
    pos['key'] = 1

    # Jointure croisée et suppression de la colonne clé
    result = pd.merge(pos, adm, on='key').drop("key", 1)

    # Ajout de la date d'exécution
    now = timezone.now()
    result['no_date_execution'] = now
    # Calcul de la notePondéré
    result['no_score_administre'] = notePonderee(result['a_filiere_futur_id'].to_numpy(),
                                                result['a_nf_futur'].to_numpy(),
                                                result['a_liste_id_competences'].to_numpy(),
                                                result['p_filiere_id'].to_numpy(), result['p_nf'].to_numpy(),
                                                result['competences'].to_numpy(),
                                                result['p_poids_filiere'].to_numpy(), result['p_poids_nf'].to_numpy(),
                                                result['p_poids_competences'].to_numpy())


    # Suppression de toutes les colonnes inutiles
    result.drop(result.columns.difference(['poste_pam_id','administre_pam_id','poste_id','a_id_sap','no_score_administre','no_date_execution']), 1, inplace=True)
    # Création de la colonne no_flag_cple_ideal
    result['no_flag_cple_ideal'] = False
    # Changer le nom des colonnes
    result = result.rename(columns={'a_id_sap': 'administre_id'})
    result = result.astype({'administre_id': 'object', 'no_flag_cple_ideal': 'object'})
    result['pam_id'] = pam_id
    
    logger.debug('end scoring')
    return result

def notations_liste(sv_id, pam_id, l_a_id, l_p_id):
    """
    Remplit la colonne note de la table Notations

    :type sv_id: char
    :param sv_id: sous vivier étudié


    :return:    - **notations** (*dataframe*): table des notations avec les notes complétées


    """
    # Sélection des postes sur lesquels aucune décision n'est prise
    logger.debug('start scoring')

    colonnes_pos = ('id','poste_id', 'poste__competences', 'poste__p_filiere_id', 'poste__p_nf', 'p_avis_pam')
    colonnes_pos_name = ('poste_pam_id','poste_id', 'competences', 'p_filiere_id', 'p_nf', 'p_avis_pam')
    statut_poste =[AvisPoste.P1,AvisPoste.P2,AvisPoste.P3,AvisPoste.P4]

    postes = Postes_Pams.objects.values_list(*colonnes_pos).filter(Q(p_pam_id=pam_id) 
                                        & Q(poste__sous_viviers=sv_id) 
                                        & Q(decisions__de_decision__isnull=True)
                                        & Q(p_avis_pam__in=statut_poste)
                                         & Q(poste_id__in=l_p_id))

    if not postes.exists:
        logger.debug(f"Pas de poste avec le bon statut {statut_poste} dans le sous vivier {sv_id} et le pam {pam_id}.")
        raise Exception("Aucun poste avec le bon statut.")

    pos = pd.DataFrame.from_records(postes, columns=colonnes_pos_name)
    pos=pos.groupby(['poste_pam_id','poste_id','p_filiere_id', 'p_nf', 'p_avis_pam'])['competences'].apply(list).reset_index(name='competences')


    # Remplire les poids des postes
    p_poids = ['p_poids_filiere', 'p_poids_nf', 'p_poids_competences']
    try:
        pos['p_poids'] = pos.apply(lambda x: Poste.objects.get(p_id=x['poste_id']).p_poids_filiere_nf_competences, axis=1)
        pos[p_poids] = pd.DataFrame(pos['p_poids'].tolist(), index=pos.index)

    except Exception as e:
        raise Exception("Tous les postes sont déjà affectés à un adminstré : Veuillez en sélectionner de nouveau")

    # Sélection des administrés dont le statut PAM est "à muter" et sur lesquels aucune décision n'est prise
    colonnes_adm = ('id','administre__a_id_sap', 'administre__a_filiere_futur_id',
                    'administre__a_nf_futur', 'administre__a_liste_id_competences')

    colonnes_adm_name = ('administre_pam_id', 'a_id_sap', 'a_filiere_futur_id',
                        'a_nf_futur', 'a_liste_id_competences')

    statut = [StatutPamChoices.A_MUTER,StatutPamChoices.A_ETUDIER]

    administres = Administres_Pams.objects.filter(pam_id=pam_id, administre__sous_vivier_id=sv_id, a_statut_pam_annee__in=statut,administre_id__in=l_a_id,
                                            decision__de_decision__isnull=True).values_list(*colonnes_adm)

    if not administres.exists:
        logger.debug(f"Pas de poste avec le bon statut {statut} dans le sous vivier {sv_id} et le pam {pam_id}.")
        raise Exception(f"Aucun administrés avec le bon statut.")

    adm = pd.DataFrame.from_records(administres, columns=colonnes_adm_name)

    if len(adm) == 0:
        raise Exception("Arret du scoring 0 administrés dont le statut PAM est à muter et sur lesquels aucune décision n'est prise")

    logger.debug('Nb admin traités : %s', len(adm))
    logger.debug('Nb groupe_poste traités : %s', len(pos))

    adm=adm.groupby(['administre_pam_id','a_id_sap','a_filiere_futur_id','a_nf_futur'])['a_liste_id_competences'].apply(list).reset_index(name='a_liste_id_competences')

    
    # Ajout de la colonne Key avec la valeur 1 aux deux bases de données, pour pouvoir effectuer la jointure croisée.
    adm['key'] = 1
    pos['key'] = 1

    # Jointure croisée et suppression de la colonne clé
    result = pd.merge(pos, adm, on='key').drop("key", 1)

    # Ajout de la date d'exécution
    now = timezone.now()
    result['no_date_execution'] = now
    # Calcul de la notePondéré
    result['no_score_administre'] = notePonderee(result['a_filiere_futur_id'].to_numpy(),
                                                result['a_nf_futur'].to_numpy(),
                                                result['a_liste_id_competences'].to_numpy(),
                                                result['p_filiere_id'].to_numpy(), result['p_nf'].to_numpy(),
                                                result['competences'].to_numpy(),
                                                result['p_poids_filiere'].to_numpy(), result['p_poids_nf'].to_numpy(),
                                                result['p_poids_competences'].to_numpy())


    # Suppression de toutes les colonnes inutiles
    result.drop(result.columns.difference(['poste_pam_id','administre_pam_id','poste_id','a_id_sap','no_score_administre','no_date_execution']), 1, inplace=True)
    # Création de la colonne no_flag_cple_ideal
    result['no_flag_cple_ideal'] = False
    # Changer le nom des colonnes
    result = result.rename(columns={'a_id_sap': 'administre_id'})
    result = result.astype({'administre_id': 'object', 'no_flag_cple_ideal': 'object'})
    result['pam_id'] = pam_id
    
    logger.debug('end scoring')
    return result



# TODO:Revoir les entrées de la fonction notation_test
# fonction notation pour le test
def notations_test():
    """
    Remplit la colonne note de la table Notations


    :return:    - **notations** (*dataframe*): table des notations avec les notes complétées


    """

    # test dataframes
    p = pd.DataFrame(
        columns=["p_id", "fonction_id", "sous_vivier_id", "formation_emploi_id", "p_liste_id_marques", "p_eip",
                 "p_date_fin", "p_flag_pam", "p_notes_gestionnaire", "p_notes_partagees", "p_statut_pam",
                 "p_poids_domaine",
                 "p_poids_filiere", "p_poids_nf", "p_poids_garnison", "p_gar"])
    p['p_id'] = [1, 2, 3]
    p['fonction_id'] = [2, 4, 5]
    p['sous_vivier_id'] = [6, 7, 8]
    p['formation_emploi_id'] = [9, 5, 10]
    p['p_liste_id_marques'] = ["1_P1,1_P2,1_P4", "1_P1,2_C,1_P4", "2_T,1_P2,1_P4"]
    p['p_eip'] = ['MAIGMA3a', 'ADMAES3b', 'SICEDR3a']
    p['p_date_fin'] = ['', '', '']
    p['p_flag_pam'] = [1, 1, 0]
    p['p_gar'] = [13, 14, 18]
    p['p_notes_gestionnaire'] = ['', '', '']
    p['p_notes_partagees'] = ['', '', '']
    p['p_statut_pam'] = ['P4', 'P4', 'P4']
    p['p_poids_domaine'] = [0.25, 0.25, 0.25]
    p['p_poids_filiere'] = [0.25, 0.25, 0.25]
    p['p_poids_nf'] = [0.25, 0.25, 0.25]
    p['p_poids_garnison'] = [0.25, 0.25, 0.25]

    a_bis = {'a_id_sap': [9984, 56754, 78905],
             'formation_emploi_id': [2, 4, 6],
             'fonction_id': [3, 78, 90],
             'sous_vivier_id': [5, 8, 98],
             'grade_id': [4, 9, 5],
             'a_liste_id_marques': ['[,,,]', '[,,,]', '[,,,]'],
             'a_nom': ['Nom_1', 'Nom_2', 'Nom_3'],
             'a_prenom': ['Prenom_1', 'Prenom_2', 'Prenom_3'],
             'a_sexe': ['M', 'F', 'M'],
             'a_id_def': [4, 5, 6],
             'a_eip': ['MAIGMA3a', 'ADMAES3b', 'SICEDR3a'],
             'a_domaine': ['MAI', 'ADM', 'SIC'],
             'a_filiere': ['GMA', 'AES', 'EDR'],
             'a_nf': ['3a', '3b', '3a'],
             'a_domaine_gestion': ['', '', ''],
             'a_date_entree_service': ['', '', ''],
             'a_gar': [13, 14, 18],
             'a_arme': ['', '', ''],
             'a_rg_origine_recrutement': ['', '', ''],
             'a_date_naissance': ['', '', ''],
             'a_diplome_HL': ['', '', ''],
             'a_dernier_diplome': ['', '', ''],
             'a_credo_fe': ['', '', ''],
             'a_date_arrivee_fe': ['', '', ''],
             'a_date_pos_statuaire': ['', '', ''],
             'a_pos_statuaire': ['', '', ''],
             'a_interuption_service': ['', '', ''],
             'a_situation_fam': ['', '', ''],
             'a_nombre_enfants': [2, 6, 9],
             'a_eis': ['', '', ''],
             'a_sap_conjoint': [987, 456, 908],
             'a_flag_particulier': [0, 1, 0],
             'a_flag_pam': [1, 0, 1],
             'a_statut_pam': ['non etudié', 'affecté', 'affecté'],
             'a_notes_gestionnaire': ['', '', ''],
             'a_notes_partagees': ['', '', ''],
             'a_eip_futur': ['', '', ''],
             'a_affectation1': ['', '', ''],
             'a_affectation2': ['', '', ''],
             'a_affectation3': ['', '', '']
             }
    a = pd.DataFrame(data=a_bis)

    notation = pd.DataFrame(
        columns=["poste_id", "administres_id", "pam_id", "no_date_execution", "no_score_administre",
                 "no_flag_cple_ideal"])

    for i in range(len(p)):
        p_id = p.loc[i, "p_id"]
        p_dom = p.loc[i, "p_eip"][0:3]
        p_fil = p.loc[i, "p_eip"][3:6]
        p_nf = p.loc[i, "p_eip"][6:]
        p_gar = p.loc[i, "p_gar"]
        p_poids_dom = 0.25
        p_poids_fil = 0.25
        p_poids_nf = 0.25
        p_poids_gar = 0.25

        for j in range(len(a)):
            a_id_sap = a.at[j, 'a_id_sap']
            a_dom = a.at[j, 'a_eip'][0:3]
            a_fil = a.at[j, 'a_eip'][4:7]
            a_nf = a.at[j, 'a_eip'][8:]
            a_gar = a.at[j, 'a_gar']

            n = notePonderee(a_dom, a_fil, a_nf, a_gar, p_dom, p_fil, p_nf, p_gar, p_poids_dom, p_poids_fil, p_poids_nf,
                             p_poids_gar)

            new_row = {'administres_id': a_id_sap,
                       'poste_id': p_id,
                       'no_date_execution': '14/06/2021',
                       'no_note_militaire': n,
                       "no_flag_cple_ideal": 0
                       }
            notation = notation.append(new_row, ignore_index=True)

    return notation
