import os
import requests
import logging
import json
import base64
import io
import pandas

from zipfile import ZipFile, ZIP_DEFLATED
from requests import Response
from requests.exceptions import RequestException
from pandas.errors import ParserError
from pathlib import Path
from inspqcommun.identity.keycloak_tools import KeycloakEnvironment

class PefiClient():
    __base_headers = {
        "Content-type": "application/json"
    }
    __pefi_base_uri = "/PEFI"
    __kc_auth_client_id = None
    def __init__(self, pefi_auth_client_id:str=None, logger=None):
        self.__pefi_url = os.environ.get('PEFI_URL')
        self.__env = os.environ.get('ENV') if 'ENV' in os.environ else 'LOCAL'
        self.__pefi_client = os.environ.get('PEFI_CLIENT_ID') if 'PEFI_CLIENT_ID' in os.environ else "sipmiportablepefiservice{env}".format(env=self.__env)
        if "PEFI_AUTH_CLIENT_ID" in os.environ:
            self.__kc_auth_client_id = os.environ.get("PEFI_AUTH_CLIENT_ID")
        elif pefi_auth_client_id is not None:
            self.__kc_auth_client_id = pefi_auth_client_id
        else:
            self.__kc_auth_client_id = "appmas{env}".format(env=self.__env.lower())
        self.__kc_env = KeycloakEnvironment(defaultAuthClientId=self.__kc_auth_client_id)
        self.__client_roles = {
            self.__pefi_client: {
                'roles': [
                    "connection-service-pefi"
                ]
            }
        }
        self.__logger = logger if logger is not None else logging.Logger()


    def __login(self):
        self.__kc_headers = self.__kc_env.authenticate(
            client_roles=self.__client_roles,
            given_name="Usager",
            family_name="Pefi",
            client_id=self.__kc_auth_client_id)
        self.__headers = self.__base_headers | self.__kc_headers
        
    def __get_pefi(self, uri:str, parameters:dict={}) -> Response:
        try:
            self.__login()
            if self.__pefi_url is None:
                self.__logger.error("L'URL du PEFI n'a pas été défini. Le définir avec la variable d'environnement PEFI_URL")
            url = "{base_url}{base_uri}".format(base_url=self.__pefi_url,
                                                base_uri=uri)
            response = requests.get(url=url,
                                    headers=self.__headers,
                                    params=parameters)
            
            return response
        except Exception as e:
            self.__logger.error(e)
            raise e
    
    def get_nom_fichiers_par_type_et_source(self, type:str, source:str) -> list[str]:
        params = {
            "typeChargement": type,
            "sourceChargement": source
        }
        response = self.__get_pefi(uri="/PEFI/chargements/names", parameters=params)
        self.__logger.info("Appel PEFI code {code}".format(code=response.status_code))
        if response.status_code >= 400:
            self.__logger.error("Erreur {code} lors de l'appel au PEFI: {erreur}".format(
                code=response.status_code,
                erreur=response.reason
            ))
        elif response.status_code == 200:
            self.__logger.info("Réponse du PEFI: {}".format(response.content.decode()))
            liste_fichiers = json.loads(response.content)
            return liste_fichiers
        

    def get_statut_fichier(self,
                           type:str,
                           source:str,
                           fichier:str) -> Response:
        
        uri = "/PEFI/chargements/names/{nom_fichier}/status".format(nom_fichier=fichier)
        params = {
            "typeChargement": type,
            "sourceChargement": source
        }
        response = self.__get_pefi(uri=uri, parameters=params)
        return response
    
    def telecharger_fichiers_par_type_et_source(self,
                                                type:str,
                                                source:str,
                                                fichier:str='latest') -> list[dict]:
        liste_fichiers = []
        try:
            if fichier == 'latest':
                uri = "/PEFI/chargements/latest"
            else:
                uri = "/PEFI/chargements/names/{nom_fichier}".format(nom_fichier=fichier)
            params = {
                "typeChargement": type,
                "sourceChargement": source
            }
            response = self.__get_pefi(uri=uri,parameters=params)
            self.__logger.info("Appel PEFI code {code}".format(code=response.status_code))
            if response.status_code >= 400:
                self.__logger.error("Erreur {code} lors de l'appel au PEFI: {erreur}".format(
                    code=response.status_code,
                    erreur=response.reason
                ))
                
            elif response.status_code == 200:
                # zip_file = base64.b64decode(response.content)
                zip_file = response.content
                with open("/tmp/{type}_{source}_{fichier}.zip".format(type=type, source=source, fichier=fichier), 'wb') as f:
                    f.write(zip_file)
                    f.close()

                z = ZipFile(io.BytesIO(zip_file))

                for file_info in z.filelist:
                    self.__logger.info(file_info.filename)
                    data_file = z.read(file_info.filename)
                    with open("/tmp/{}".format(file_info.filename),'w+') as f:
                        f.write(data_file.decode("iso-8859-1"))
                        f.close
                        fichier_donnees = {}
                        fichier_donnees["nom"] = file_info.filename
                        fichier_donnees["data"] = pandas.read_csv(
                            io.BytesIO(data_file),
                            sep=';',
                            header=None,
                            skiprows=0,
                            encoding='iso-8859-1')
                        liste_fichiers.append(fichier_donnees)
        except ParserError as e:
            self.__logger.error(e)
            raise e
        
        return liste_fichiers
        
    def deposer_fichiers(self,
                        type:str,
                        source:str,
                        nom:str,
                        source_dir:str,
                        zip:bool=True) -> Response:
        uri = "/chargements/{nom_fichier}".format(nom_fichier=nom)
        params = {
                "typeChargement": type,
                "sourceChargement": source
            }
        try:
            fichier = None
            if zip:
                fichier = self.__zip_directory_in_memory(source_dir=source_dir)
            else:
                fichier = self.__read_first_file_in_directory(source_dir=source_dir)

            if fichier is not None:
                self.__login()
                url = f"{self.__pefi_url}{self.__pefi_base_uri}{uri}"
                response = requests.post(url=url,
                                        headers=self.__headers,
                                        params=params,
                                        data=fichier)
                return response
            else:
                self.__logger.error("Le fichier a déposer dans PEFI n'a pas pu être lu.")
                return None
        except RequestException as e:
            self.__logger.error(e)


    def __zip_directory_in_memory(self, source_dir:str) -> bytes:
        zip_buffer = io.BytesIO()
        with ZipFile(zip_buffer, 'w', ZIP_DEFLATED) as zipf:
            for foldername, subfolders, filenames in os.walk(source_dir):
                for filename in filenames:
                    filepath = os.path.join(foldername, filename)
                    arcname = os.path.relpath(filepath, source_dir)
                    with open(filepath, 'rb') as f:
                        zipf.writestr(arcname, f.read())
        zip_buffer.seek(0)
        encoded = base64.b64encode(zip_buffer.read())
        return encoded

    def __read_first_file_in_directory(self, source_dir:str) -> bytes:
        for foldername, subfolders, filenames in os.walk(source_dir):
            for filename in filenames:
                filepath = os.path.join(foldername, filename)
                with open(filepath, 'rb') as f:
                    encoded = base64.b64encode(f.read())
                return encoded
        return None

