from datetime import datetime
import time
import datetime

from django.http import Http404
from rest_framework.exceptions import APIException
from rest_framework.permissions import IsAdminUser, IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
import datetime

from ..models import Administre, PAM
from ..models import StatutPamChoices as StatutPam
from ..serializers import AlimentationSerializer
from ..utils.alimentation_decorators import (data_perf_logger_factory,
                                             get_data_logger)
from ..utils.decorators import execution_time, query_count
from ..utils.extraction.administre import (to_table_administres_bo)
from ..utils.insertion.administre import (insert_administre_bo,
                                          update_administre_fmob)
from ..utils_extraction import (DataFrameTypes, FileTypes, read_files_by_type,
                                to_table_administre_notation,
                                to_table_affectation, to_table_diplome,
                                to_table_domaines, to_table_fe,
                                to_table_filieres, to_table_fmob_femp,
                                to_table_fmob_fmob,
                                to_table_fonctions, to_table_fud,
                                to_table_garnisons, to_table_groupesMarques,
                                to_table_marques, to_table_pam, to_table_postes,
                                to_table_ref_gest, to_table_ref_org,
                                to_table_ref_sv_fil, to_table_reo_ocv,
                                to_table_sous_vivier,
                                to_table_zone_geographique)
from ..utils_insertion import (insert_PAM, insert_administre_notation, insert_Affectation,
                               insert_Diplome, insert_Filiere,
                               insert_FMOB_femp, insert_FMOB_fmob,
                               insert_Fonction,
                               insert_FormationEmploi, insert_Fud,
                               insert_Garnison, insert_Grade, insert_Marque,
                               insert_MarquesGroupe, insert_Poste,
                               insert_RefFeMere, insert_RefGest, insert_RefOrg,
                               insert_RefSvFil, insert_SousVivier,
                               insert_SousVivier_instances,
                               insert_ZoneGeographique, update_domaine,
                               update_m2m_links_gestionnaire, update_poste_ocv, insert_delta)

import pandas as pd


class AlimentationView(APIView):
    """
    Cette page est l'alimentation principale d'Ogure.
    Elle permet à l'administrateur de charger un ensemble de fichiers pour alimenter ou mettre à jour la base de données.
    """
    permission_classes = [IsAuthenticated, IsAdminUser]
    serializer_class = AlimentationSerializer

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.logger = get_data_logger(self)


    def get(self, request):
        """La fonction get renvoie une reponse contenant Formulaire d'alimentation d'OGURE NG

        :type request: rest_framework.request.Request
        :param request: Request


        :return:    - **return** (*json*): json contenant "Formulaire d'alimentation d'OGURE NG".
        """
        return Response("Formulaire d'alimentation d'OGURE NG")


    @execution_time(logger_factory=data_perf_logger_factory)
    @query_count(logger_factory=data_perf_logger_factory)
    def post(self, request):
        """La fonction post charge les fichiers.

        :type request: rest_framework.request.Request
        :param request: Request contenant les fichiers

        :return:    - **Response** (*Response*): Reponse contient les erreurs de chargement des données .
        """
        try:
            # TODO: préparer une variable qui renvoie les erreurs sous 3 axes : données référentielles, administrés, postes
            serializer = self.serializer_class(data=request.data)
            serializer.is_valid(raise_exception=True)

            self.logger.info('---------------------Upload begining---------------------------')
            start_time = time.time()

            file_donnees_bo = serializer.validated_data.get('Donnees_BO_ADT')
            file_reo = serializer.validated_data.get('REO')
            file_reo_suivant =  serializer.validated_data.get('REO_PAM_SUIVANT')
            file_reo_ocv = serializer.validated_data.get('REO_OCV')
            file_ref_gest = serializer.validated_data.get('referentiel_gestionnaire')
            file_ref_org = serializer.validated_data.get('referentiel_organique')
            file_ref_sv_fil = serializer.validated_data.get('refeferentiel_sous_vivier_filiere')
            file_ref_fe = serializer.validated_data.get('referentiel_fe')
            file_fmob = serializer.validated_data.get('FMOB')
            file_fmob_suivant = serializer.validated_data.get('FMOB_PAM_SUIVANT')
            file_filiere_domaine = serializer.validated_data.get('domaine_filiere')
            file_insee = serializer.validated_data.get('insee_maping')
            file_diplomes = serializer.validated_data.get('diplomes')
            file_fud = serializer.validated_data.get('FUD')
            file_ref_zones_geo = serializer.validated_data.get('ref_zones_geo')

            self.logger.info('---------------------Upload ending-----------------------------')
            self.logger.debug("--------------upload time -- %d seconds ------------------------", time.time() - start_time)

            df_by_type = read_files_by_type({
                FileTypes.BO:                       file_donnees_bo,
                FileTypes.DIPLOME:                  file_diplomes,
                FileTypes.DOM_FIL:                  file_filiere_domaine,
                FileTypes.FMOB_FEMP:                file_fmob,
                FileTypes.FMOB_FEMP_PAM_SUIVANT:    file_fmob_suivant,
                FileTypes.FUD:                      file_fud,
                FileTypes.INSEE:                    file_insee,
                FileTypes.REF_FE:                   file_ref_fe,
                FileTypes.REF_GEO:                  file_ref_zones_geo,
                FileTypes.REF_GEST:                 file_ref_gest,
                FileTypes.REF_ORG:                  file_ref_org,
                FileTypes.REF_SV_FIL:               file_ref_sv_fil,
                FileTypes.REO:                      file_reo,
                FileTypes.REO_PAM_SUIVANT:          file_reo_suivant,
                FileTypes.REO_OCV:                  file_reo_ocv,
            })

            DF = DataFrameTypes
            diplomes_df = df_by_type.get(DF.DIPLOME)
            donnees_bo_df = df_by_type.get(DF.BO)
            femp_df = df_by_type.get(DF.FEMP)
            filiere_domaine_df = df_by_type.get(DF.DOM_FIL)
            fmob_df = df_by_type.get(DF.FMOB)
            fud_df = df_by_type.get(DF.FUD)
            insee_df = df_by_type.get(DF.INSEE)
            ref_fe_df = df_by_type.get(DF.REF_FE)
            ref_gest_df = df_by_type.get(DF.REF_GEST)
            ref_org_df = df_by_type.get(DF.REF_ORG)
            ref_sv_fil_df = df_by_type.get(DF.REF_SV_FIL)
            ref_zones_geo_df = df_by_type.get(DF.REF_GEO)
            reo_df = df_by_type.get(DF.REO)
            reo_ocv_df = df_by_type.get(DF.REO_OCV)

            #dataframe pam + 1
            femp_suivant_df = df_by_type.get(DF.FEMP_PAM_SUIVANT)
            fmob_suivant_df = df_by_type.get(DF.FMOB_PAM_SUIVANT)
            reo_suivant_df = df_by_type.get(DF.REO_PAM_SUIVANT)


            self.logger.info('-------------------- Insert beginning ---------------------')
            start_time_insert = time.time()
            text_response = []

 

            if ref_sv_fil_df is not None:
                sv_cree, sv_modifie, sv_erreur, sv_supprime = insert_SousVivier(to_table_sous_vivier(ref_sv_fil_df))
                text_response.append([f"Référentiel de sous-viviers/filières : {sv_cree} SousVivier créés, {sv_modifie} SousVivier mis à jour, {sv_erreur} SousVivier en erreur et {sv_supprime} SousVivier supprimés."])
                self.logger.info('Sous-viviers ---------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : sous-viviers (nécessite %s)', DF.REF_SV_FIL.value[1])

            if ref_org_df is not None:
                ref_org_cree, ref_org_modifie, ref_org_erreur, ref_org_supprime = insert_RefOrg(to_table_ref_org(ref_org_df))
                text_response.append([f"Référentiel organique : {ref_org_cree} RefOrg créés, {ref_org_modifie} RefOrg mis à jour, {ref_org_erreur} RefOrg en erreur et {ref_org_supprime} RefOrg supprimés."])
                self.logger.info('Référentiel organique ------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : référentiel organique')

            if ref_gest_df is not None:
                ref_gest_cree, ref_gest_modifie, ref_gest_erreur, ref_gest_supprime, user_cree, user_modifie, user_supprime, user_ignore = insert_RefGest(to_table_ref_gest(ref_gest_df))
                text_response.append([f"Référentiel de gestionnaires : {ref_gest_cree} RefGest créés, {ref_gest_modifie} RefGest mis à jour, {ref_gest_erreur} RefGest en erreur et {ref_gest_supprime} RefGest supprimés."])
                text_response.append([f"Référentiel de gestionnaires : {user_cree} User créés, {user_modifie} User mis à jour, {user_supprime} User supprimés et {user_ignore} User ignorés."])
                self.logger.info('Référentiel gestionnaire ---------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : référentiel de gestionnaires')

            if ref_sv_fil_df is not None:
                ref_sv_cree, ref_sv_delete, ref_sv_erreur, ref_sv_ignore = insert_RefSvFil(to_table_ref_sv_fil(ref_sv_fil_df))
                text_response.append([f"Référentiel de sous-viviers/filières : {ref_sv_cree} RefSvFil créés, {ref_sv_delete} RefSvFil supprimés, {ref_sv_erreur} RefSvFil en erreur et {ref_sv_ignore} RefSvFil ignorés."])
                self.logger.info('Référentiel sous-vivier filière --------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : référentiel de sous-viviers/filières')

            if ref_gest_df is not None or ref_org_df is not None or ref_sv_fil_df is not None:
                update_m2m_links_gestionnaire('SV')
                self.logger.info('Liens gestionnaires/sous-viviers -------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : liens gestionnaires/sous-viviers (nécessite %s ou %s ou %s)',
                                 DF.REF_GEST.value[1], DF.REF_ORG.value[1], DF.REF_SV_FIL.value[1])

            if filiere_domaine_df is not None:
                update_domaine(to_table_domaines(filiere_domaine_df))
                self.logger.info('Domaine --------------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : domaines (nécessite %s)', DF.DOM_FIL.value[1])

            if insee_df is not None and donnees_bo_df is not None:
                gar_cree, gar_maj , error = insert_Garnison(to_table_garnisons(insee_df, donnees_bo_df))
                text_response.append([f"INSEE et Données BO : {gar_cree} garnisons crées, {gar_maj} garnisons mises à jour, {error} garnisons en erreur."])
                self.logger.info('Garnison -------------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : garnisons (nécessite %s + %s)', DF.INSEE.value[1], DF.BO.value[1])

            if reo_df is not None:
                reo_cree = insert_Fonction(to_table_fonctions(reo_df))
                text_response.append([f"REO : {reo_cree} fonctions de postes créés."])
                self.logger.info('Fonction -------------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : fonctions (nécessite %s)', DF.REO.value[1])
 
            if reo_suivant_df is not None:
                reo_suivant_cree = insert_Fonction(to_table_fonctions(reo_suivant_df))
                text_response.append([f"REO : {reo_suivant_cree} fonctions de postes A+1 créés."])
                self.logger.info('Fonction A+1 -------------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : fonctions A + 1 (nécessite %s)', DF.REO_PAM_SUIVANT.value[1])
            

            # TODO if ....
            insert_Grade()
            self.logger.info('Grade ----------------------------------------------> Succès')

            if filiere_domaine_df is not None:
                dom_fil_cree, dom_fil_modifie, dom_fil_erreur, dom_fil_supprime = insert_Filiere(to_table_filieres(filiere_domaine_df))
                text_response.append([f"Domaines - filières : {dom_fil_cree} couples domaine/filière créés, {dom_fil_modifie} couples domaine/filière mis à jour, {dom_fil_erreur} couples domaine/filière en erreur et {dom_fil_supprime} couples domaine/filière supprimés."])
                self.logger.info('Filières ------------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : filières (nécessite %s)', DF.DOM_FIL.value[1])

            # TODO if ....
            insert_MarquesGroupe(to_table_groupesMarques())
            self.logger.info('MarquesGroupe -------------------------------------> Succès')

            # TODO if ....
            insert_Marque(to_table_marques())
            self.logger.info('Marque --------------------------------------------> Succès')

            if ref_fe_df is not None:
                df = to_table_fe(ref_fe_df)
                self.logger.debug('Extraction des données du référentiel FE ----------> Succès')
                fe_cree, fe_maj, error_count = insert_FormationEmploi(df)
                text_response.append([f"Référentiel FE : {fe_cree} formation d'emplois créées, {fe_maj} formation d'emplois mises à jour, {error_count} formation d'emplois en erreur."])                
                self.logger.info('FormationEmplois ----------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : formations-emplois (nécessite %s)', DF.REF_FE.value[1])

            if ref_fe_df is not None:
                insert_RefFeMere(ref_fe_df)
                self.logger.info('Référentiel FE mère -------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : formations-emplois mères (nécessite %s)', DF.REF_FE.value[1])

            if ref_gest_df is not None or ref_org_df is not None or ref_fe_df is not None:
                update_m2m_links_gestionnaire('FE')
                self.logger.info('Liens gestionnaires/formations-emplois ------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : liens gestionnaires/formations-emplois (nécessite %s ou %s ou %s)',
                                 DF.REF_GEST.value[1], DF.REF_ORG.value[1], DF.REF_FE.value[1])

            if donnees_bo_df is not None:
                bo_adm_cree, bo_adm_modifie, bo_adm_deja_modifie, bo_adm_erreur, bo_adm_ignore, bo_dom_ignore, bo_fil_ignore = insert_administre_bo(to_table_administres_bo(donnees_bo_df))
                text_response.append([f"Données BO : {bo_adm_cree} administrés créés, {bo_adm_modifie} administrés mis à jour, {bo_adm_deja_modifie} adminsitrés déjà à jour, {bo_adm_erreur} administrés en erreur, {bo_adm_ignore} administrés ignorés, {bo_dom_ignore} domaines ignorés et {bo_fil_ignore} filières ignorées."])
                self.logger.info('Administre ----------------------------------------> Succès')

            else:
                self.logger.info('Mise à jour ignorée : administrés (nécessite %s)', DF.BO.value[1])

            if diplomes_df is not None:
                diplome_cree, diplome_maj, diplome_sup = insert_Diplome(to_table_diplome(diplomes_df))
                text_response.append([f"Diplômes : {diplome_cree} diplômes créés, {diplome_maj} diplômes mis à jour, {diplome_sup} supprimés."])
                self.logger.info('Diplome -------------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : diplômes (nécessite %s)', DF.DIPLOME.value[1])

            if donnees_bo_df is not None:
                donnee_bo_cree, donnee_bo_sup = insert_Affectation(to_table_affectation(donnees_bo_df))
                text_response.append([f"Données BO : {donnee_bo_cree} affectation créés, {donnee_bo_sup} affectation mis à jour."])
                self.logger.info('Affectations --------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : affectations (nécessite %s)', DF.BO.value[1])

            if fud_df is not None:
                fud_cree, fud_sup = insert_Fud(to_table_fud(fud_df))
                text_response.append([f"FUD : {fud_cree} FUD créés, {fud_sup} FUD supprimées."])
                self.logger.info('FUD -----------------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : FUD (nécessite %s)', DF.FUD.value[1])

            if donnees_bo_df is not None:
                bo_cree, bo_maj, error_count = insert_administre_notation(to_table_administre_notation(donnees_bo_df))
                text_response.append([f"Données BO : {bo_cree} notations créées, {bo_maj} notations mises à jour, {error_count} notations en erreurs."])
                self.logger.info('Administre Notation -------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : administrés/notations (nécessite %s)', DF.BO.value[1])

            if reo_df is not None:
                annee_pam = PAM.objects.get(pam_statut='PAM en cours').pam_id
                reo_cree, reo_suivant_cree, reo_modifie, reo_erreur, reo_ignore = insert_Poste(to_table_postes(reo_df),annee_pam)
                text_response.append([f"REO : {reo_cree} postes créés, {reo_modifie} postes mis à jour, {reo_erreur} postes en erreur et {reo_ignore} postes ignorés."])
                self.logger.info('Poste ---------------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : postes (nécessite %s)', DF.REO.value[1])

            if reo_suivant_df is not None:
                annee_pam = PAM.objects.get(pam_statut='PAM A+1').pam_id
                reo_cree, reo_suivant_cree, reo_modifie, reo_erreur, reo_ignore = insert_Poste(to_table_postes(reo_suivant_df),annee_pam)
                #info reo
                text_response.append([f"REO A + 1 : {reo_suivant_cree} postes A+1 créés, {reo_erreur} postes A+1 en erreur et {reo_ignore} postes A+1 ignorés."])
                self.logger.info('Poste A+1 ---------------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : postes A + 1 (nécessite %s)', DF.REO_PAM_SUIVANT.value[1])

            if reo_suivant_df is not None:
                self.logger.info('Calcul du delta de la colonne Info REO, veuillez patienter ...')
                insert_delta(to_table_postes(reo_suivant_df))
                text_response.append([f"Mise à jour de la colonne info REO"])
                self.logger.info('Calcul du delta Info Reo ---------------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour de la colonne "info reo" ignorée')

            # TODO if ....
            insert_SousVivier_instances()
            self.logger.info('Sous-viviers instances ---------------------------> Succès')

            if reo_ocv_df is not None:
                reo_ocv_modifie, reo_ocv_erreur, reo_ocv_ignore = update_poste_ocv(to_table_reo_ocv(reo_ocv_df))
                text_response.append([f"Requêtes OCV : {reo_ocv_modifie} postes-ocv mis à jour, {reo_ocv_erreur} postes-ocv en erreur et {reo_ocv_ignore} postes-ocv ignorés."])
                self.logger.info('Poste-ocv -----------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : postes/administrés (nécessite %s)', DF.REO_OCV.value[1])

            if ref_zones_geo_df is not None:
                ref_zones_geo_df_cree, ref_zones_geo_df_sup, error_count = insert_ZoneGeographique(to_table_zone_geographique(ref_zones_geo_df))
                text_response.append([f"Référentiels Zones Géographiques : {ref_zones_geo_df_cree} zones-geo créées, {ref_zones_geo_df_sup} zones-geo supprimés et  {error_count} zones-geo en erreur."])
                self.logger.info('Référentiel zones géographiques -------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : zones géographiques (nécessite %s)', DF.REF_GEO.value[1])

            if fmob_df is not None:
                fmob_cree, fmob_maj, ignore_count = insert_FMOB_fmob(to_table_fmob_fmob(fmob_df))
                text_response.append([f"Formulaire de mobilité : {fmob_cree} fmob créés, {fmob_maj} fmob mis à jour et  {ignore_count} fmob ignorés."])
                self.logger.debug('Fichier FMOB --------------------------------------> Succès')    
            else:
                self.logger.info('Mise à jour ignorée : FMOB (nécessite %s)', DF.FMOB.value[1])

            if femp_df is not None:
                femp_cree, femp_maj, ignore_count = insert_FMOB_femp(to_table_fmob_femp(femp_df))
                text_response.append([f"Formulaire de mobilité : {femp_cree} femp créés, {femp_maj} femp mis à jour et  {ignore_count} femp ignorés."])
                self.logger.debug('Fichier FEMP --------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : FEMP (nécessite %s)', DF.FEMP.value[1])

            if fmob_suivant_df is not None:
                fmob_cree, fmob_maj, ignore_count = insert_FMOB_fmob(to_table_fmob_fmob(fmob_suivant_df))
                text_response.append([f"Formulaire de mobilité A+1 : {fmob_cree} fmob A+1 créés, {fmob_maj} fmob A+1 mis à jour et  {ignore_count} fmob A+1 ignorés."])
                self.logger.debug('Fichier FMOB A+1--------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : FMOB A + 1(nécessite %s)', DF.FMOB_PAM_SUIVANT.value[1])

            if femp_suivant_df is not None:
                femp_cree, femp_maj, ignore_count = insert_FMOB_femp(to_table_fmob_femp(femp_suivant_df))
                text_response.append([f"Formulaire de mobilité A+1 : {femp_cree} femp A+1 créés, {femp_maj} femp A+1 mis à jour et  {ignore_count} femp A+1 ignorés."])
                self.logger.debug('Fichier FEMP A+1 --------------------------------------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : FEMP A + 1(nécessite %s)', DF.FEMP_PAM_SUIVANT.value[1])


            if fmob_df is not None:
                a_maintenir, a_traiter = update_administre_fmob(to_table_fmob_fmob(fmob_df))
                text_response.append([f"Formulaire de mobilité : {a_maintenir} administrés mis à jour en 'A maintenir' car FMOB annulé, {a_traiter} administrés mis à jour en 'A muter' car ils ont un FMOB."])
                self.logger.debug('Mise à jour du statut PAM si annulation FMOB et si FMOB existe------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : statut PAM si annulation FMOB (nécessite %s ou %s)', DF.BO.value[1], DF.FMOB.value[1])

            if fmob_suivant_df is not None:
                a_maintenir, a_traiter = update_administre_fmob(to_table_fmob_fmob(fmob_suivant_df))
                text_response.append([f"Formulaire de mobilité A+1 : {a_maintenir} administrés A+1 mis à jour en 'A maintenir' car FMOB annulé, {a_traiter} administrés A+1 mis à jour en 'A muter' car ils ont un FMOB A+1."])
                self.logger.debug('Mise à jour du statut PAM A+1 si annulation FMOB et si FMOB existe ------> Succès')
            else:
                self.logger.info('Mise à jour ignorée : statut PAM A+1 si annulation FMOB (nécessite %s ou %s)', DF.BO.value[1], DF.FMOB_PAM_SUIVANT.value[1])   



            self.logger.debug('Administres Pams -------------------------> Success')

            self.logger.debug('--------------- Insert time : %d seconds -----------------', time.time() - start_time_insert)
            self.logger.info('---------------------- Insert ending ----------------------')

            text_response_df = pd.DataFrame(text_response)
            if text_response_df.empty:
                return Response(text_response_df)
            else:
                return Response(text_response_df[0])
        except (Http404, APIException):
            raise
        except BaseException:
            message = "Impossible d'alimenter le(s) référentiel(s)"
            self.logger.exception(message)
            raise APIException(message)



