import datetime
from enum import Enum, EnumMeta, unique
from typing import Any, Dict, List, Union

import numpy as np
import pandas as pd


def excel_date_converter(value) -> Union[str, float]:
    """
    convertit la valeur en date (sans information de temps)
    note : type(np.nan)  =>  <class 'float'>
    """
    if isinstance(value, datetime.datetime):
        return value.date().isoformat()
    if isinstance(value, str):
        val = '-'.join(list(reversed(value.split('/'))))
        return datetime.datetime.strptime(val, "%Y-%m-%d").date().isoformat() if val else np.nan
    return str(value) if value else np.nan


def excel_int64_converter(value) -> Union[int, float]:
    """
    convertit la valeur en entier
    note : type(np.nan)  =>  <class 'float'>
    """
    if isinstance(value, int):
        return value
    val = value.strip() if isinstance(value, str) else value
    return np.int64(val) if val else np.nan


def excel_uint64_converter(value) -> Union[int, float]:
    """
    convertit la valeur en entier non signé
    note : type(np.nan)  =>  <class 'float'>
    """
    if isinstance(value, int):
        return value
    val = value.strip() if isinstance(value, str) else value
    return np.uint64(val) if val else np.nan


class FileCols(str, Enum):
    """ Classe de base pour les colonnes d'un fichier """

    converter: str

    def __new__(metacls, value: str, converter=None):
        obj = str.__new__(metacls, value)
        obj._value_ = value
        obj.converter = converter
        return obj

    def __repr__(self):
        return self.__str__()

    @classmethod
    def columns(cls, *args: 'FileCols') -> List[str]:
        """
        Renvoie les noms de colonnes. Deux cas :
        - il n'y a pas d'argument : tous les noms de colonnes
        - il y a des arguments : uniquement leurs noms de colonnes

        :param args: membres de l'enum
        :type  args: class:`FileCols` (multiple)

        :return: noms de colonnes
        :rtype:  List[str]
        """
        return [member.value for member in (args or cls)]

    @classmethod
    def col_mapping(cls, mapping: Dict[Union[str, 'FileCols'], Any]) -> Dict[str, Any]:
        """
        Renvoie un mapping de noms pour utiliser avec pandas.DataFrame.rename.
        Les clés du dictionnaire initial peut être des membres de l'enum, elles seront converties
        en noms de colonnes.

        :param mapping: correspondances initiales
        :type  mapping: Dict[Union[str, FileCols], Any]

        :return: nouvelles correspondances
        :rtype:  Dict[str, Any]
        """
        return {k.value if isinstance(k, cls) else k: v for k, v in mapping.items()}

    @classmethod
    def converters(cls, *args: 'FileCols') -> Dict[str, Any]:
        """
        Renvoie un mapping de conversion pour Pandas. Deux cas :
        - il n'y a pas d'argument : mapping de toutes les colonnes qui ont un convertisseur explicite
        - il y a des arguments : mapping des colonnes fournies qui ont un convertisseur explicite

        :return: mapping entre noms de colonnes et convertisseur
        :rtype:  Dict[str, Any]
        """
        return {member.value: member.converter for member in (args or cls) if member.converter is not None}


class BOCols(FileCols):
    """ Colonnes du fichier BO """

    AFFECTATION_1                    = ('Affectation -1 L',                            str)                     # AV
    AFFECTATION_2                    = ('Affectation -2 L',                            str)                     # AX
    AFFECTATION_3                    = ('Affectation -3 L',                            str)                     # AZ
    AFFECTATION_4                    = ('Affectation -4 L',                            str)                     # BB
    AFFECTATION_5                    = ('Affectation -5 L',                            str)                     # BD
    AFFECTATION_6                    = ('Affectation -6 L',                            str)                     # BF
    AFFECTATION_7                    = ('Affectation -7 L',                            str)                     # BH
    AFFECTATION_8                    = ('Affectation -8 L',                            str)                     # BJ
    AFFECTATION_9                    = ('Affectation -9 L',                            str)                     # BL
    AGE_ANNEES                       = ('Age en années (au 31/12)',                    excel_uint64_converter)  # CG
    ANNEE_NOTATION                   = ('Année notation A',                            excel_uint64_converter)  # CL
    ANNEE_NOTATION_1                 = ('Année notation A-1',                          excel_uint64_converter)  # CR
    ANNEE_NOTATION_2                 = ('Année notation A-2',                          excel_uint64_converter)  # CX
    ANNEE_NOTATION_3                 = ('Année notation A-3',                          excel_uint64_converter)  # DD
    ANNEE_NOTATION_4                 = ('Année notation A-4',                          excel_uint64_converter)  # DJ
    ANNEE_NOTATION_5                 = ('Année notation A-5',                          excel_uint64_converter)  # DP
    APTITUDE_EMPLOI_SUP              = ('Apt resp / Emp sup A',                        str)                     # CP
    APTITUDE_EMPLOI_SUP_1            = ('Apt resp / Emp sup A-1',                      str)                     # CV
    APTITUDE_EMPLOI_SUP_2            = ('Apt resp / Emp sup A-2',                      str)                     # DB
    APTITUDE_EMPLOI_SUP_3            = ('Apt resp / Emp sup A-3',                      str)                     # DH
    APTITUDE_EMPLOI_SUP_4            = ('Apt resp / Emp sup A-4',                      str)                     # DN
    APTITUDE_EMPLOI_SUP_5            = ('Apt resp / Emp sup A-5',                      str)                     # DT
    ARME                             = ('Arme',                                        str)                     # L
    CREDO_FE                         = ('CREDO FE act',                                str)                     # X
    DATE_AFFECTATION_1               = ('Affectation -1 DD',                           excel_date_converter)    # AU
    DATE_AFFECTATION_2               = ('Affectation -2 DD',                           excel_date_converter)    # AW
    DATE_AFFECTATION_3               = ('Affectation -3 DD',                           excel_date_converter)    # AY
    DATE_AFFECTATION_4               = ('Affectation -4 DD',                           excel_date_converter)    # BA
    DATE_AFFECTATION_5               = ('Affectation -5 DD',                           excel_date_converter)    # BC
    DATE_AFFECTATION_6               = ('Affectation -6 DD',                           excel_date_converter)    # BE
    DATE_AFFECTATION_7               = ('Affectation -7 DD',                           excel_date_converter)    # BG
    DATE_AFFECTATION_8               = ('Affectation -8 DD',                           excel_date_converter)    # BI
    DATE_AFFECTATION_9               = ('Affectation -9 DD',                           excel_date_converter)    # BK
    DATE_ARRIVEE_FE                  = ('Date arrivée FE',                             excel_date_converter)    # AA
    DATE_DEBUT_GRADE                 = ('Grade act DD',                                excel_date_converter)    # AH
    DATE_DERNIER_ACR                 = ('Dernière mutation ACR D',                     excel_date_converter)    # AC
    DATE_ENTREE_SERVICE              = ('Entrée en Service',                           excel_date_converter)    # N
    DATE_FONCTION_1                  = ('Fonction -1 DD',                              excel_date_converter)    # BM
    DATE_FONCTION_2                  = ('Fonction -2 DD',                              excel_date_converter)    # BO
    DATE_FONCTION_3                  = ('Fonction -3 DD',                              excel_date_converter)    # BQ
    DATE_FONCTION_4                  = ('Fonction -4 DD',                              excel_date_converter)    # BS
    DATE_FONCTION_5                  = ('Fonction -5 DD',                              excel_date_converter)    # BU
    DATE_FONCTION_6                  = ('Fonction -6 DD',                              excel_date_converter)    # BW
    DATE_FONCTION_7                  = ('Fonction -7 DD',                              excel_date_converter)    # BY
    DATE_FONCTION_8                  = ('Fonction -8 DD',                              excel_date_converter)    # CA
    DATE_FONCTION_9                  = ('Fonction -9 DD',                              excel_date_converter)    # CC
    DATE_FUD                         = ('Date prise effet FUD départ',                 excel_date_converter)    # DV
    DATE_LIEN_SERVICE                = ('Lien au service DF',                          excel_date_converter)    # EG
    DATE_NAISSANCE                   = ('Naissance',                                   excel_date_converter)    # R
    DATE_POSITION_STATUAIRE          = ('Date Position statutaire',                    excel_date_converter)    # AF
    DATE_RDC                         = ('Date Radiation des contrôles',                excel_date_converter)    # W
    DATE_STATUT_CONCERTO             = ('Situation administrative act DD',             excel_date_converter)    # DX
    DATE_STATUT_CONCERTO_FUTUR       = ('Date Position statu future',                  excel_date_converter)    # DZ
    DATE_MARIAGE                     = ('Situation familiale (IT0002) DD',             excel_date_converter)    # CH
    DERNIER_DIPLOME                  = ('Dernier diplôme militaire LA',                str)                     # V
    DIPLOME_PLUS_HAUT_NIVEAU         = ('Diplôme militaire de plus haut niveau',       str)                     # U
    DOMAINE                          = ('Domaine EIP act LA',                          str)                     # I
    DOMAINE_GESTION                  = ('Domaine de gestion act LA',                   str)                     # H
    DOMAINE_POSTE                    = ('Domaine emploi occupé act LA',                str)                     # ED
    EIP                              = ('EIP act LA',                                  str)                     # F
    EIS                              = ('EIS act LA',                                  str)                     # G
    ENFANTS                          = ('Enfants',                                     str)                     # EK
    FILIERE                          = ('Filière EIP act LA',                          str)                     # J
    FILIERE_POSTE                    = ('Nature de filière  emploi occupé act LA',     str)                     # EE
    FONCTION                         = ('Fonction act L',                              str)                     # T
    FONCTION_1                       = ('Fonction -1 L',                               str)                     # BN
    FONCTION_2                       = ('Fonction -2 L',                               str)                     # BP
    FONCTION_3                       = ('Fonction -3 L',                               str)                     # BR
    FONCTION_4                       = ('Fonction -4 L',                               str)                     # BT
    FONCTION_5                       = ('Fonction -5 L',                               str)                     # BV
    FONCTION_6                       = ('Fonction -6 L',                               str)                     # BX
    FONCTION_7                       = ('Fonction -7 L',                               str)                     # BZ
    FONCTION_8                       = ('Fonction -8 L',                               str)                     # CB
    FONCTION_9                       = ('Fonction -9 L',                               str)                     # CD
    FUD                              = ('Type de FUD départ')                                                   # DW
    GARNISON                         = ('Garnison act',                                str)                     # Z
    GRADE                            = ('GRADE TA',                                    str)                     # B
    ID_DEF                           = ('Identifiant défense',                         str)                     # E
    ID_DEF_CONJOINT                  = ('Identifiant défense du conjoint militaire',   str)                     # AN
    ID_SAP                           = ('Matricule SAP',                               excel_int64_converter)   # A
    ID_SAP_CONJOINT                  = ('Identifiant SAP du conjoint militaire',       excel_int64_converter)   # AM
    INTERRUPTION_SERVICE             = ('Interruption de service',                     str)                     # AG
    MARQUEUR_PN                      = ('Marquant Personnel Navigant')                                          # EH
    NF                               = ('Niveau fonctionnel EIP act',                  str)                     # K
    NF_POSTE                         = ('Niveau fonctionnel emploi occupé act C',      str)                     # EF
    NOM                              = ('NOM',                                         str)                     # C
    NOMBRE_ENFANTS                   = ("Nbr total d'enfants",                         excel_uint64_converter)  # O
    NR_OU_IRIS                       = ('IRIS / RAC retenu A',                         excel_int64_converter)   # CN
    NR_OU_IRIS_1                     = ('IRIS / RAC retenu A-1',                       excel_int64_converter)   # CT
    NR_OU_IRIS_2                     = ('IRIS / RAC retenu A-2',                       excel_int64_converter)   # CZ
    NR_OU_IRIS_3                     = ('IRIS / RAC retenu A-3',                       excel_int64_converter)   # DF
    NR_OU_IRIS_4                     = ('IRIS / RAC retenu A-4',                       excel_int64_converter)   # DL
    NR_OU_IRIS_5                     = ('IRIS / RAC retenu A-5',                       excel_int64_converter)   # DR
    ORIGINE_RECRUTEMENT              = ('Origine recrutement LA',                      str)                     # Q
    PLS_GB_MAX                       = ('PLS GB Max')                                                           # EI
    POSITION_STATUTAIRE              = ('Position statutaire act L',                   str)                     # AE
    POTENTIEL_RESPONSABILITE_SUP     = ('Potentiel responsabilités catégorie sup A',   str)                     # CQ
    POTENTIEL_RESPONSABILITE_SUP_1   = ('Potentiel responsabilités catégorie sup A-1', str)                     # CW
    POTENTIEL_RESPONSABILITE_SUP_2   = ('Potentiel responsabilités catégorie sup A-2', str)                     # DC
    POTENTIEL_RESPONSABILITE_SUP_3   = ('Potentiel responsabilités catégorie sup A-3', str)                     # DI
    POTENTIEL_RESPONSABILITE_SUP_4   = ('Potentiel responsabilités catégorie sup A-4', str)                     # DO
    POTENTIEL_RESPONSABILITE_SUP_5   = ('Potentiel responsabilités catégorie sup A-5', str)                     # DU
    PRENOM                           = ('Prénom',                                      str)                     # D
    PROFESSION_CONJOINT              = ('Profession conjoint Act L',                   str)                     # AL
    RAC_OU_IRIS_CUMULE               = ('NR/NGC cumulé A',                             excel_int64_converter)   # CM
    RAC_OU_IRIS_CUMULE_1             = ('NR/NGC cumulé A-1',                           excel_int64_converter)   # CS
    RAC_OU_IRIS_CUMULE_2             = ('NR/NGC cumulé A-2',                           excel_int64_converter)   # CY
    RAC_OU_IRIS_CUMULE_3             = ('NR/NGC cumulé A-3',                           excel_int64_converter)   # DE
    RAC_OU_IRIS_CUMULE_4             = ('NR/NGC cumulé A-4',                           excel_int64_converter)   # DK
    RAC_OU_IRIS_CUMULE_5             = ('NR/NGC cumulé A-5',                           excel_int64_converter)   # DQ
    REGROUPEMENT_ORIGINE_RECRUTEMENT = ('Regroupement origine recrutement C',          str)                     # M
    RF_QSR                           = ('QSR A',                                       str)                     # CO
    RF_QSR_1                         = ('QSR A-1',                                     str)                     # CU
    RF_QSR_2                         = ('QSR A-2',                                     str)                     # DA
    RF_QSR_3                         = ('QSR A-3',                                     str)                     # DG
    RF_QSR_4                         = ('QSR A-4',                                     str)                     # DM
    RF_QSR_5                         = ('QSR A-5',                                     str)                     # DS
    SEXE                             = ('Sexe',                                        str)                     # CJ
    SEXE_CONJOINT                    = ('Sexe du conjoint',                            str)                     # AT
    SITUATION_FAMILIALE              = ('Situation familiale (IT0002) L',              str)                     # CI
    STATUT_CONCERTO                  = ('Situation admi actuelle',                     str)                     # DY
    STATUT_CONCERTO_FUTUR            = ('Position statutaire future',                  str)                     # EA


class FmobCols(FileCols):
    """ Colonnes du fichier FMOB """

    ID_SAP = ('Matricule SAP', excel_int64_converter)  # A


class InseeCols(FileCols):
    """ Colonnes du fichier INSEE """

    CODE_INSEE  = ('CODE INSEE',    str)                    # D
    CODE_POSTAL = ('CODE POSTAL',   str)                    # C
    CREDO_FE    = ('CREDO FE act',  str)                    # B
    ID_SAP      = ('Matricule SAP', excel_int64_converter)  # A


class ReoCols(FileCols):
    """ Colonnes du fichier REO """

    ANNEE_PROJET     = ('Année Projet',                str)  # B
    CATEGORIE        = ('Catégorie /EFF CELL',         str)  # P
    CODE_NF          = ('NFEO',                        str)  # W
    CODE_POSTAL      = ('Code Postal /OB G',           str)  # J
    DOMAINE          = ('DEO',                         str)  # U
    DOMAINE_GESTION  = ('Postes NFS',                  str)  # AA
    EIP              = ('MGS gestionnaire / EFF CELL', str)  # T
    FONCTION_ID      = ('ETR Code / EFF CELL',         str)  # Y
    FONCTION_LIBELLE = ('ETR Libellé / EFF CELL',      str)  # Z
    FORMATION_EMPLOI = ('Code CREDO Long OB',          str)  # F
    FILIERE          = ('FEO',                         str)  # V
    ID_POSTE         = ('N° De Poste',                 str)  # X


