"""
Ce module contient les Utilitaires de matching
"""
# import des pré requis
import time

import pandas as pd
from matching.games import HospitalResident

from .models import Notation, Poste, PreferencesListe, StatutPamChoices, AvisPosteChoices as AvisPoste
from .utils.logging import get_logger

logger = get_logger(__name__)

# Fonction de  preprocessing
def preprocess_matching(pam_id,sv_id,df_pref):
    """
    Retourne les liste de souhait des militaires et des postes

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


    :return:    - **gs_proposants** (*liste*): liste des proposants
            - **gs_disposants** (*liste*): liste des disposants
            - **gs_capacite** (*liste*): liste des capacités pour chaque disposants

    """
    logger.debug('preprocessing matching %s', sv_id)

    statut_adm = [StatutPamChoices.A_MUTER,StatutPamChoices.A_ETUDIER]
    statut_poste =[AvisPoste.P1,AvisPoste.P2,AvisPoste.P3,AvisPoste.P4]
    # liste_preference = PreferencesListe.objects.filter(pam_id=pam_id,administre_pam__administre__sous_vivier_id=sv_id).values()
    notation = Notation.objects.filter(pam_id=pam_id,
                                       administre_pam__administre__sous_vivier_id=sv_id, 
                                       administre_pam__a_statut_pam_annee__in=statut_adm,
                                       administre_pam__decision__de_decision__isnull=True,
                                       poste_pam__poste__sous_viviers=sv_id,
                                       poste_pam__decisions__de_decision__isnull=True,
                                       poste_pam__p_avis_pam__in=statut_poste).values()

    liste_preference_df = df_pref
    notation_df = pd.DataFrame.from_records(notation)
    liste_admin_lp = liste_preference_df.drop_duplicates(subset=['administre_id'])
    gs_proposants = {}
    gs_disposants = {}
    gs_capacite = {}
    l2 = []
    if not liste_preference_df.empty:
        for admin in liste_admin_lp['administre_id']:
            l = []
            new_lp_df = liste_preference_df.loc[liste_preference_df['administre_id'] == admin].drop_duplicates(subset=['poste_id'])
            new_lp_df = new_lp_df.drop_duplicates(subset=['lp_rang_poste'])
            new_lp_df = new_lp_df.sort_values(
                by=['lp_rang_poste']).reset_index()

            for i in range(len(new_lp_df)):
                l.append(new_lp_df.at[i, 'poste_id'])
                l2.append(new_lp_df.at[i, 'poste_id'])
            gs_proposants[admin] = l
        l2 = list(set(l2))

        notation_df = notation_df[notation_df.administre_id.isin(liste_admin_lp['administre_id'])]

        for poste in l2:
            l1 = []
            new_no_df = notation_df.loc[notation_df['poste_id'] == poste].sort_values(by=['no_score_administre'],
                                                                                      ascending=False).reset_index()
            for i in range(len(new_no_df)):
                if poste in gs_proposants[new_no_df.at[i, 'administre_id']]:
                    l1.append(new_no_df.at[i, 'administre_id'])
            gs_disposants[poste] = l1
            gs_capacite[poste] = 1
    logger.debug("Administre proposants : %s", gs_proposants)
    logger.debug("Poste disposants : %s", gs_disposants)
    logger.debug('end preprocessing matching func')
    return gs_proposants, gs_disposants, gs_capacite

# Fonction de  preprocessing
def preprocess_matchingSelectif(sv_id, pam_id,l_a_id,l_p_id, df_pref):
    """
    Retourne les liste de souhait des militaires et des postes

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


    :return:    - **gs_proposants** (*liste*): liste des proposants
            - **gs_disposants** (*liste*): liste des disposants
            - **gs_capacite** (*liste*): liste des capacités pour chaque disposants

    """
    logger.debug('preprocessing matching %s', sv_id)

    statut_adm = [StatutPamChoices.A_MUTER,StatutPamChoices.A_ETUDIER]
    statut_poste =[AvisPoste.P1,AvisPoste.P2,AvisPoste.P3,AvisPoste.P4]
    notation = Notation.objects.filter(pam_id=pam_id,
                                       administre_pam__administre__sous_vivier_id=sv_id, 
                                       administre_pam__a_statut_pam_annee__in=statut_adm,
                                       administre_pam__administre_id__in=l_a_id,
                                       administre_pam__decision__de_decision__isnull=True,
                                       poste_pam__poste__sous_viviers=sv_id,
                                       poste_pam__decisions__de_decision__isnull=True,
                                       poste_pam__p_avis_pam__in=statut_poste).values()

    liste_preference_df = df_pref
    notation_df = pd.DataFrame.from_records(notation)
    liste_admin_lp = liste_preference_df.drop_duplicates(subset=['administre_id'])
    gs_proposants = {}
    gs_disposants = {}
    gs_capacite = {}
    l2 = []
    if not liste_preference_df.empty:
        for admin in liste_admin_lp['administre_id']:
            l = []
            new_lp_df = liste_preference_df.loc[liste_preference_df['administre_id'] == admin].drop_duplicates(subset=['poste_id'])
            new_lp_df = new_lp_df.drop_duplicates(subset=['lp_rang_poste'])
            new_lp_df = new_lp_df.sort_values(
                by=['lp_rang_poste']).reset_index()

            for i in range(len(new_lp_df)):
                l.append(new_lp_df.at[i, 'poste_id'])
                l2.append(new_lp_df.at[i, 'poste_id'])
            gs_proposants[admin] = l
        l2 = list(set(l2))

        notation_df = notation_df[notation_df.administre_id.isin(liste_admin_lp['administre_id'])]

        for poste in l2:
            l1 = []
            new_no_df = notation_df.loc[notation_df['poste_id'] == poste].sort_values(by=['no_score_administre'],
                                                                                      ascending=False).reset_index()
            for i in range(len(new_no_df)):
                if poste in gs_proposants[new_no_df.at[i, 'administre_id']]:
                    l1.append(new_no_df.at[i, 'administre_id'])
            gs_disposants[poste] = l1
            gs_capacite[poste] = 1
    logger.debug("Administre proposants : %s", gs_proposants)
    logger.debug("Poste disposants : %s", gs_disposants)
    logger.debug('end preprocessing matching func')

    return gs_proposants, gs_disposants, gs_capacite


# Fonction de matching
def matching_parfait(gs_proposants, gs_disposants, gs_capacite):
    """
    Retourne le dictionnaire python presentant le resultats du matching

    :type gs_proposants: liste
    :param gs_proposants: liste des proposants
    :type gs_disposants: liste
    :param gs_disposants: liste des disposants
    :type gs_capacite: liste
    :param gs_capacite: liste des capacités pour chaque disposants


    :return:    - **matching** (*dictionnaire*): dictionnaire python presentant le resultats du matching
    """

    logger.debug('start matching parfait')
    # Traitement des entrées
    game = HospitalResident.create_from_dictionaries(
        gs_proposants, gs_disposants, gs_capacite
    )
    # Creation du jeu
    matching = game.solve(optimal='hospital')
    # Test de validation et de stabilité
    logger.debug('Validity Check : %s', game.check_validity())
    assert game.check_validity()
    logger.debug('Stability Check : %s', game.check_stability())
    assert game.check_stability()

    # Creation des listes d'affectés et de non afféctes
    matched_residents = []
    for _, residents in matching.items():
        for resident in residents:
            matched_residents.append(resident.name)
    unmatched_residents = set(gs_proposants.keys()) - set(matched_residents)

    # KPI matching
    logger.debug("Nb demandeurs : %s", len(gs_proposants))
    logger.debug("Poste demandeurs : %s", gs_proposants)
    logger.debug("Nb affectés : %s", len(matched_residents))
    logger.debug("Administres affectés : %s", matched_residents)
    logger.debug("Nb non affecte : %s", len(unmatched_residents))
    logger.debug("Administres non affecte : %s", unmatched_residents)
    logger.debug('end matching parfait')

    return (matching)
