import logging

import numpy as np
import pandas as pd
from backend.models.administre import Administre
from backend.models.administre import StatutPamChoices as StatutPam
from backend.models.domaine import Domaine
from backend.models.filiere import Filiere
from backend.models.fonction import Fonction
from backend.models.formation_emploi import FormationEmploi
from backend.models.grade import Grade
from ..alimentation import BOCols
from ..alimentation_decorators import data_perf_logger_factory, get_data_logger
from ..decorators import execution_time

logger = get_data_logger(__name__)


@execution_time(logger_factory=data_perf_logger_factory)
def to_table_administres_bo(bo: pd.DataFrame) -> pd.DataFrame:
    """
    Création de la table Administrés à partir du fichier de données BO. Sélection et renommage des champs.

    :param bo: table BO
    :type  bo: class:`pandas.DataFrame`

    :return: data frame contenant les information des administrés
    :rtype:  class:`pandas.DataFrame`
    """
    Cols = Administre.Cols

    # import des tables contenant les clés étrangères
    fonctions = pd.DataFrame.from_records(Fonction.objects.all().values())
    domaines = pd.DataFrame.from_records(Domaine.objects.all().values())
    filieres = pd.DataFrame.from_records(Filiere.objects.all().values())

    grades = pd.DataFrame.from_records(Grade.objects.all().values())
    fe = pd.DataFrame.from_records(FormationEmploi.objects.all().values())

    # sélection des attributs nécessaires à la table administres
    col_adm = BOCols.columns(
        BOCols.ID_SAP,
        BOCols.CREDO_FE,
        BOCols.FONCTION,
        BOCols.GRADE,
        BOCols.DATE_DEBUT_GRADE,
        BOCols.NOM,
        BOCols.PRENOM,
        BOCols.SEXE,
        BOCols.ID_DEF,
        BOCols.EIP,
        BOCols.EIS,
        BOCols.DOMAINE,
        BOCols.FILIERE,
        BOCols.NF,
        BOCols.DOMAINE_GESTION,
        BOCols.DATE_ENTREE_SERVICE,
        BOCols.ARME,
        BOCols.REGROUPEMENT_ORIGINE_RECRUTEMENT,
        BOCols.DATE_NAISSANCE,
        BOCols.DIPLOME_PLUS_HAUT_NIVEAU,
        BOCols.DATE_RDC,
        BOCols.DATE_DERNIER_ACR,
        BOCols.DERNIER_DIPLOME,
        BOCols.DATE_ARRIVEE_FE,
        BOCols.POSITION_STATUTAIRE,
        BOCols.DATE_POSITION_STATUAIRE,
        BOCols.INTERRUPTION_SERVICE,
        BOCols.SITUATION_FAMILIALE,
        BOCols.DATE_MARIAGE,
        BOCols.NOMBRE_ENFANTS,
        BOCols.ENFANTS,
        BOCols.ID_DEF_CONJOINT,
        BOCols.FONCTION_1,
        BOCols.FONCTION_2,
        BOCols.FONCTION_3,
        BOCols.FONCTION_4,
        BOCols.FONCTION_5,
        BOCols.FONCTION_6,
        BOCols.FONCTION_7,
        BOCols.FONCTION_8,
        BOCols.FONCTION_9,
        BOCols.DATE_FONCTION_1,
        BOCols.DATE_FONCTION_2,
        BOCols.DATE_FONCTION_3,
        BOCols.DATE_FONCTION_4,
        BOCols.DATE_FONCTION_5,
        BOCols.DATE_FONCTION_6,
        BOCols.DATE_FONCTION_7,
        BOCols.DATE_FONCTION_8,
        BOCols.DATE_FONCTION_9,
        BOCols.NF_POSTE,
        BOCols.DOMAINE_POSTE,
        BOCols.FILIERE_POSTE,
        BOCols.PLS_GB_MAX,
        BOCols.MARQUEUR_PN,
        BOCols.PROFESSION_CONJOINT,
        BOCols.ID_SAP_CONJOINT,
        BOCols.SEXE_CONJOINT,
        BOCols.ORIGINE_RECRUTEMENT,
        BOCols.DATE_LIEN_SERVICE,
        BOCols.AGE_ANNEES,
        BOCols.STATUT_CONCERTO,
        BOCols.DATE_STATUT_CONCERTO,
        BOCols.STATUT_CONCERTO_FUTUR,
        BOCols.DATE_STATUT_CONCERTO_FUTUR,
        BOCols.FUD,
        BOCols.DATE_FUD
    )
    administres = bo[col_adm]

    # jointure avec les tables contenant les clés étrangères
    # mapping avec les postes
    administres = administres.merge(fonctions, how='left', left_on=BOCols.FONCTION.value, right_on='fon_libelle')
    logger.debug("Nombre d'administrés dans le fichier")
    logger.debug('total administres : %s', administres.shape[0])
    administres = administres.merge(grades, how='inner', left_on=BOCols.GRADE.value, right_on='gr_code')
    logger.debug("Filtrage par grade reconnu")
    logger.debug("Nombre d'administres : %s", administres.shape[0])
    administres = administres.merge(fe, how='inner', left_on=BOCols.CREDO_FE.value, right_on='fe_code')
    logger.debug("Filtrage par FE reconnue")
    logger.debug("Nombre d'administres : %s", administres.shape[0])

    # sélection et renommage des champs (BIEN FAIRE LE MAPPING DES COLONNES QU'ON VEUT GARDER)
    adm_mapping = BOCols.col_mapping({
        BOCols.ID_SAP:                           Cols.PK,
        'fe_code':                               f'{Cols.REL_FORMATION_EMPLOI}_id',
        'fon_id':                                "a_code_fonction",
        BOCols.GRADE:                            f'{Cols.REL_GRADE}_id',
        BOCols.DATE_DEBUT_GRADE:                 "a_grade_date_debut",
        BOCols.FONCTION:                         "a_fonction",
        BOCols.NOM:                              "a_nom",
        BOCols.PRENOM:                           "a_prenom",
        BOCols.SEXE:                             "a_sexe",
        BOCols.ID_DEF:                           "a_id_def",
        BOCols.EIP:                              "a_eip",
        BOCols.EIP:                              "a_eip_fiche_detaille",
        BOCols.EIS:                              "a_eis",
        BOCols.DOMAINE:                          Cols.REL_DOMAINE,
        BOCols.FILIERE:                          Cols.REL_FILIERE,
        BOCols.NF:                               'a_nf',
        BOCols.DOMAINE_GESTION:                  "a_domaine_gestion",
        BOCols.DATE_ENTREE_SERVICE:              "a_date_entree_service",
        BOCols.ARME:                             "a_arme",
        BOCols.REGROUPEMENT_ORIGINE_RECRUTEMENT: "a_rg_origine_recrutement",
        BOCols.DATE_RDC:                         'a_date_rdc',
        BOCols.DATE_DERNIER_ACR:                 'a_date_dernier_acr',
        BOCols.DATE_NAISSANCE:                   "a_date_naissance",
        BOCols.DIPLOME_PLUS_HAUT_NIVEAU:         "a_diplome_hl",
        BOCols.DERNIER_DIPLOME:                  "a_dernier_diplome",
        BOCols.CREDO_FE:                         "a_credo_fe",
        BOCols.DATE_ARRIVEE_FE:                  "a_date_arrivee_fe",
        BOCols.POSITION_STATUTAIRE:              "a_pos_statuaire",
        BOCols.DATE_POSITION_STATUAIRE:          "a_date_pos_statuaire",
        BOCols.INTERRUPTION_SERVICE:             "a_interruption_service",
        BOCols.SITUATION_FAMILIALE:              "a_situation_fam",
        BOCols.DATE_MARIAGE:                     "a_date_mariage",
        BOCols.NOMBRE_ENFANTS:                   "a_nombre_enfants",
        BOCols.ENFANTS:                          "a_enfants",
        BOCols.ID_SAP_CONJOINT:                  "a_sap_conjoint",
        BOCols.FONCTION_1:                       "a_fonction1",
        BOCols.FONCTION_2:                       "a_fonction2",
        BOCols.FONCTION_3:                       "a_fonction3",
        BOCols.FONCTION_4:                       "a_fonction4",
        BOCols.FONCTION_5:                       "a_fonction5",
        BOCols.FONCTION_6:                       "a_fonction6",
        BOCols.FONCTION_7:                       "a_fonction7",
        BOCols.FONCTION_8:                       "a_fonction8",
        BOCols.FONCTION_9:                       "a_fonction9",
        BOCols.DATE_FONCTION_1:                  "a_date_fonction1",
        BOCols.DATE_FONCTION_2:                  "a_date_fonction2",
        BOCols.DATE_FONCTION_3:                  "a_date_fonction3",
        BOCols.DATE_FONCTION_4:                  "a_date_fonction4",
        BOCols.DATE_FONCTION_5:                  "a_date_fonction5",
        BOCols.DATE_FONCTION_6:                  "a_date_fonction6",
        BOCols.DATE_FONCTION_7:                  "a_date_fonction7",
        BOCols.DATE_FONCTION_8:                  "a_date_fonction8",
        BOCols.DATE_FONCTION_9:                  "a_date_fonction9",
        BOCols.NF_POSTE:                         "a_nf_poste",
        BOCols.DOMAINE_POSTE:                    "a_domaine_poste",
        BOCols.FILIERE_POSTE:                    "a_filiere_poste",
        BOCols.PLS_GB_MAX:                       "a_pls_gb_max",
        BOCols.MARQUEUR_PN:                      "a_marqueur_pn",
        BOCols.PROFESSION_CONJOINT:              "a_profession_conjoint",
        BOCols.ID_DEF_CONJOINT:                  "a_id_def_conjoint",
        BOCols.SEXE_CONJOINT:                    "a_sexe_conjoint",
        BOCols.ORIGINE_RECRUTEMENT:              "a_origine_recrutement",
        BOCols.STATUT_CONCERTO:                  Cols.STATUT_CONCERTO,
        BOCols.DATE_STATUT_CONCERTO:             Cols.DATE_STATUT_CONCERTO,
        BOCols.STATUT_CONCERTO_FUTUR:            Cols.STATUT_CONCERTO_FUTUR,
        BOCols.DATE_STATUT_CONCERTO_FUTUR:       Cols.DATE_STATUT_CONCERTO_FUTUR,
        BOCols.FUD:                              'a_fud',
        BOCols.DATE_FUD:                         'a_date_fud',
        BOCols.DATE_LIEN_SERVICE:                "a_lien_service",
        BOCols.AGE_ANNEES:                       "a_age_en_annees"
    })
    administres = administres.rename(columns=adm_mapping, errors='raise')

    # initialisation des colonnes vides
    adm_col_vides = {
        'a_liste_id_marques':   '',
        'a_notes_gestionnaire': '',
        'a_notes_partagees':    '',
        'a_flag_particulier':   0
    }
    for k, v in adm_col_vides.items():
        administres[k] = v

    administres = administres[list(adm_mapping.values()) + list(adm_col_vides.keys())]

    administres_dom = administres.merge(domaines, how='inner', left_on=Cols.REL_DOMAINE, right_on='d_code')
    df = pd.merge(administres, administres_dom, on=[Cols.PK, Cols.REL_DOMAINE], how="left", indicator=True)
    df = df[df['_merge'] == 'left_only']
    domaines_not_found = df[Cols.REL_DOMAINE].drop_duplicates().tolist()
    administres_excluded_by_dom = df[Cols.PK].tolist()
    logger.debug("Filtrage par domaine actuel reconnu")
    logger.debug("Nombre d'administres : %s", administres_dom.shape[0])
    logger.debug('Domaines non retrouvés : %s', domaines_not_found)
    logger.debug('****************')
    logger.debug(administres_excluded_by_dom)
    logger.debug('****************')
    administres_fil = administres_dom.merge(filieres, how='inner', left_on=Cols.REL_FILIERE, right_on='f_code')
    # administres.merge(administres_fil, how='')
    df = pd.merge(administres_dom, administres_fil, on=[Cols.PK, Cols.REL_FILIERE], how="left", indicator=True)
    df = df[df['_merge'] == 'left_only']
    filieres_not_found = df[Cols.REL_FILIERE].drop_duplicates().tolist()
    administres_excluded_by_fil = df[Cols.PK].tolist()
    logger.debug("Filtrage par filière reconnue")
    logger.debug("Nombre d'administres restant : %s", administres_fil.shape[0])
    logger.debug('Filières non retrouvées : %s', filieres_not_found)
    logger.debug('****************')
    logger.debug(administres_excluded_by_fil)
    logger.debug('****************')
    administres = administres_fil

    administres = administres.merge(domaines, how='left', left_on='a_domaine_poste', right_on='d_code', indicator=True)
    administres.loc[administres['_merge'] == 'left_only', 'a_domaine_poste'] = None
    administres.drop(columns='_merge', inplace=True)
    administres = administres.merge(filieres, how='left', left_on='a_filiere_poste', right_on='f_code', indicator=True)
    administres.loc[administres['_merge'] == 'left_only', 'a_filiere_poste'] = None
    # administres = administres.merge(filieres, how='inner', left_on='a_filiere_poste', right_on='f_code')

    administres['a_categorie'] = administres["a_nf"].replace(
        {'1A': 'MDR', '1B': 'MDR', '1C': 'MDR', '2.': 'SOFF', '3A': 'SOFF', '3B': 'SOFF', '3B NFS': 'SOFF', '4.': 'OFF',
         '5A': 'OFF', '5B': 'OFF', '5C': 'OFF', '6A': 'OGX', '6B': 'OGX'}, inplace=False)
    # administres["a_nom_prenom"].fillna('NOM Prenom', inplace=True)
    # # administres["a_nom_prenom"].replace('', 'NOM Prenom', inplace=True)
    # nom_prenom_split = administres['a_nom_prenom'].str.split(r" +", n=1).str
    # administres["a_nom"] = nom_prenom_split.get(0).fillna('')
    # administres["a_prenom"] = nom_prenom_split.get(1).fillna('')
    # administres.drop("a_nom_prenom", inplace=True, axis=1)
    administres['a_nom'] = administres['a_nom'].fillna('NOM')
    administres['a_prenom'] = administres['a_prenom'].fillna('Prénom')

    administres[f'{Cols.REL_FORMATION_EMPLOI}_id'] = administres[f'{Cols.REL_FORMATION_EMPLOI}_id'].replace({np.nan: None})
    administres[f'{Cols.REL_GRADE}_id'] = administres[f'{Cols.REL_GRADE}_id'].replace({np.nan: None})

    administres = administres.drop_duplicates(subset=[Cols.PK])
    logger.debug('Retrait des doublons')
    logger.debug("Nombre d'administres restants : %s", administres.shape[0])

    administres = administres.reset_index(drop=True)
    administres['a_sap_conjoint'] = administres['a_sap_conjoint'].replace({np.nan: None})
    administres['a_nf_poste'] = (administres['a_nf_poste'].replace({np.nan, None})
                                                          .apply(lambda x: str(x)[1:])
                                                          .str.upper())
    administres['a_nf'] = administres['a_nf'].str.upper()
    administres['a_eip'] = administres[Cols.REL_DOMAINE] + administres[Cols.REL_FILIERE] + administres['a_nf']
    administres['a_marqueur_pn'] = administres['a_marqueur_pn'].apply(lambda x: True if x == 'X' else False)

    administres = (administres.fillna(np.nan)
                              .replace([np.nan], [None]))
    logger.debug("Nombre d'administres extrait : %s", administres.shape[0])
    return administres


