import imaplib
import email
import os
import datetime
from tracemalloc import stop
import pandas as pd
import sqlalchemy
import threading
from urllib.parse import quote_plus

# Para desactivar la alerta de los rename de la tabla "-" a "TIPO."
pd.options.mode.chained_assignment = None

# Si hay que conectarse a una base de datos MySQL hay que instalar tambien pymysql.
patron = 'postgresql://username:password@databasehost:port/databasename'

# Funcion que se ejecuta cuando se lanza el script.
def main():
    hoy = datetime.datetime.today().strftime("%Y-%m-%d")
    cantidadCorreos = 0
    # Se comprueba si existe el fichero log del dia de hoy, si existe abre el fichero y si no lo crea.
    if not os.path.exists(f"./logs/descargarInsertarCSV_{hoy}.log"):
        open(f"./logs/descargarInsertarCSV_{hoy}.log", "x")
    with open(f"./logs/descargarInsertarCSV_{hoy}.log", 'a') as x:
        try:
            # Para establecer la conexion con la base de datos.
            contraseñaConArroba = quote_plus("T@dJn2021")
            cadenaConexion = f'mysql+pymysql://root:{contraseñaConArroba}@127.0.0.1:3306/co_demo'
            engine = sqlalchemy.create_engine(cadenaConexion)
            engine.connect()
            print("Conexion establecida con la base de datos correctamente.")
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Conexion establecida con la base de datos correctamente.\n')
        except Exception as e:
            print("Error al conectarse con la base de datos.", e)
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al conectarse con la base de datos.\n')
            main()

        # Para elegir el servidor de correo de Dinahosting tasiva y el puerto.
        try:
            imapSSL = imaplib.IMAP4_SSL(
                host="tasiva-com.correoseguro.dinaserver.com", port=993)
            print("Servidor de correo seleccionado correctamente.")
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Servidor de correo seleccionado correctamente.\n')
        except Exception as e:
            print("Error al conectar con el servidor: " + str(e))
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al conectar con el servidor.\n')
            main()
        
        # Para iniciar sesion en el servidor de correo.
        try:
            imapSSL.login("informes@tasiva.com", "ControlDatos-1")
            print("Sesion iniciado correctamente.")
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Sesion iniciada correctamente.\n')
        except Exception as e:
            print("Error al iniciar sesion en el correo electronico." + str(e))
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al iniciar sesion.\n')
            main()
        
        # Para escoger la cantidad de correos que hay en la bandeja de entrada.
        try:
            respuesta, idCorreos = imapSSL.select("INBOX")
            # La respuesta de idCorreos devuelve un string de "b'148'" por lo que se decodifica para obtener la cantidad de correos.
            cantidadCorreos = int(idCorreos[0].decode("utf-8"))
            print(f"Se van a descargar los {cantidadCorreos} correos de la bandeja de entrada.")
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Descargando los correos de la bandeja de entrada.\n')
        except Exception as e:
            print("Error al seleccionar el directorio." + str(e))
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al seleccionar el directorio.\n')
            main()
        
        try:
            # Se recorren todos los archivos, por cada archivo se hace un fetch y se llama a obtenerArchivos para descargar el archivo.
            for i in range(1, cantidadCorreos + 1):
                resultado, datos = imapSSL.fetch(str(i), "(RFC822)")
                print(f"Descargando correo {i}...")
                obtenerArchivos(email.message_from_bytes(datos[0][1]))
            print("Descarga finalizada.")
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Correos descargados correctamente.\n')
        except Exception as e:
            print("Error al leer los correos.", e.args)
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al leer los correos.\n')
            main()

        try:
            insertarBD(engine)
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Datos insertados a la base de datos correctamente.\n')
        except Exception as e:
            print("No se han podido insertar los CSVs correctamente.")
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al insertar los CSVs.\n')
            main()

        try:
            cerrarSesion(imapSSL)
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Sesion cerrada correctamente.\n')
        except Exception as e:
            print("Error al cerrar la sesion." + str(e))
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al cerrar sesion.\n')
            main()

# Para parsear y guardar el archivo CSV en la carpeta archivos.
def obtenerArchivos(mensaje):
    try:
        for parte in mensaje.walk():
            if parte.get_content_maintype() == 'multipart':
                continue
            if parte.get('Content-Disposition') is None:
                continue
            filename = parte.get_filename()
            # Verifica si el directorio archivos esta creado, si no esta creado lo crea.
            if not os.path.isdir("archivos"):
                # Crea el directorio archivos.
                os.mkdir("archivos")
            # Se recuperan todos los archivos que esten en la carpeta archivos.
            archivosCSV = os.listdir("archivos")
            # Se verifica si el archivo ya existe en la carpeta archivos.
            if filename and filename not in archivosCSV:
                with open("./archivos/" + filename, 'wb') as f:
                    f.write(parte.get_payload(decode=True))
                    print("Archivo " + filename + " descargado correctamente.")
    except Exception as e:
        print("Error al escribir el archivo CSV en la carpeta archivos.", e)
        obtenerArchivos()

# Funcion que cierra la conexion y la sesion.
def cerrarSesion(imapSSL):
    # Para cerrar la conexion.
    try:
        imapSSL.close()
    except Exception as e:
        print("Error al cerrar la conexion." + str(e))
        stop()
    # Para cerrar sesion.
    try:
        imapSSL.logout()
        print("Sesion cerrada correctamente.")
    except Exception as e:
        print("Error al cerrar la sesion." + str(e))
        stop()


# Funcion que inserta los datos del csv en la base de datos.
def insertarBD(engine):
    hoy = datetime.datetime.today().strftime("%Y-%m-%d")
    # Para que compruebe si existe el archivo y si es asi que lo abra y sino que lo cree.
    if not os.path.exists(f"./logs/descargarInsertarCSV_{hoy}.log"):
        open(f"./logs/descargarInsertarCSV_{hoy}.log", "x")
    with open(f"./logs/descargarInsertarCSV_{hoy}.log", 'a') as x:
        # Para obtener todas las tablas de la base de datos.
        try:
            resultados = engine.execute("SELECT table_name FROM information_schema.tables WHERE table_schema = 'co_demo';")
            tablas = resultados.all()
        except Exception as e:
            print('Error al obtener todas las tablas de la base de datos.')
            x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S:%f")}: Error al obtener todas las tablas de la base de datos.\n')
            insertarBD()
        for cadaTabla in tablas:
            # Para que al recorrer todas las tablas se obtenga la ultima fecha.
            ultimaFecha = engine.execute(f"SELECT FECHA FROM {cadaTabla[0]} ORDER BY FECHA DESC LIMIT 1").all()
            # Se comprueba que haya ultima fecha, si no es asi es que la tabla esta vacia.
            if len(ultimaFecha) == 0:
                # Se recorren todos los archivos CSV.
                for cadaCSV in os.listdir('./archivos'):
                    # Para obtener los datos necesarios del nombre del archivo.
                    splitNombre = cadaCSV.split("_")
                    fecha = splitNombre[2].split(".")[0]
                    numMaquina = splitNombre[1].lstrip('0')
                    splitNombreTabla = cadaTabla[0].split("_")
                    # Para comprobar que el numero de la maquina y el numero de la tabla son los mismos.
                    if numMaquina == splitNombreTabla[1]:
                        # Se lee el archivo CSV.
                        csv = pd.read_csv(f"./archivos/{cadaCSV}")
                        # Si el archivo CSV no esta vacio.
                        if not csv.empty:
                            # Para leer los datos que se van a insertar en la tabla "_estado".
                            try:
                                # IMPORTANTE poner los espacios delabte de cada columna ya que asi estan en el CSV.
                                tablaEstado = csv[["-", " PORCENTAJE NOK", " TOTALES", " ERROR PROCESADO"]]
                                tablaEstado.rename({'-': 'TIPO'}, axis=1, inplace=True)
                                # Para obtener de la tabla el porcentajeNOK.
                                porcentaje_nok = csv[" PORCENTAJE NOK"]
                                arrayID = []
                                arrayFecha = []
                                arrayEstado = []
                                # Se recorren todas las filas del CSV y se inserte la ID, la fecha y estado en un array
                                # que despues se insertara al DataFrame.
                                for i in range(0, len(tablaEstado.index)):
                                    numPorcentajeNOK = porcentaje_nok.iloc[i]
                                    estado = 1
                                    if numPorcentajeNOK >= 50:
                                        estado = 0
                                    elif numPorcentajeNOK >= 30:
                                        estado = 2
                                    arrayID.append(0)
                                    arrayFecha.append(fecha)
                                    arrayEstado.append(estado)
                                # Se insertan la id, fecha y estado al DataFrame.
                                tablaEstado.insert(0, "id", arrayID)
                                tablaEstado.insert(1, "FECHA", arrayFecha)
                                tablaEstado.insert(3, "ESTADO", arrayEstado)
                                # Se eliminan los espacios en blanco de los nombres de las tablas.
                                tablaEstado.columns = tablaEstado.columns.str.lstrip()
                            except Exception as e:
                                print(f"Error al generar el DataFrame de la tabla maquina_{numMaquina}_estado", e)
                                x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al generar el DataFrame de la tabla maquina_{numMaquina}_estado, {e}.\n')
                                insertarBD()
                            # Se intenta insertar el DataFrame en la base de datos.
                            try:
                                tablaEstado.to_sql(f"maquina_{numMaquina}_estado", engine, if_exists='append', index = False)
                                print(f"Datos de la tabla maquina_{numMaquina}_estado actualizados correctamente.")
                            except Exception as e:
                                print(f"Error al insertar los datos en la tabla maquina_{numMaquina}_estado", e)
                                x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al insertar los datos en la tabla maquina_{numMaquina}_estado.\n')
                                insertarBD()

                            # Para leer los datos que se va a insertar en la tabla "_produccion".
                            try:
                                # IMPORTANTE poner los espacios delabte de cada columna ya que asi estan en el CSV.
                                tablaProduccion = csv[["-", " TOTALES OK", " TOTALES NOK", " PORCENTAJE NOK", " ETIQUETA SUP NOK", " ETIQUETA INF NOK", " LOTE NOK", " COCCION NOK", " GRIETAS NOK", " BOCADOS NOK", " REBORDES NOK", " AGUJERO NOK", " SELLADO NOK", " OBJ EXT NOK"]]
                                tablaProduccion.rename({"-": "TIPO"}, axis=1, inplace=True)
                                arrayID = []
                                arrayFecha = []
                                arrayEstado = []
                                # Se recorren todas las filas del CSV y se inserte la ID, la fecha y estado en un array
                                # que despues se insertara al DataFrame.
                                for i in range(0, len(tablaProduccion.index)):
                                    porcentaje_nok = tablaProduccion.iloc[i][" PORCENTAJE NOK"]
                                    estado = 1
                                    if porcentaje_nok >= 50:
                                        estado = 0
                                    elif porcentaje_nok >= 30:
                                        estado = 2
                                    arrayID.append(0)
                                    arrayFecha.append(fecha)
                                    arrayEstado.append(estado)
                                # Para insertar los datos del array en el DataFrame.
                                tablaProduccion.insert(0, "id", arrayID)
                                tablaProduccion.insert(1, "FECHA", arrayFecha)
                                tablaProduccion.insert(2, "ESTADO", arrayEstado)
                                # Para eliminar los espacios en blanco de los nombres de las columnas.
                                tablaProduccion.columns = tablaProduccion.columns.str.lstrip()
                            except Exception as e:
                                print(f"Error al generar el DataFrame de la tabla maquina_{numMaquina}_produccion", e)
                                x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al generar el DataFrame de la tabla maquina_{numMaquina}_produccion.\n')
                                insertarBD()
                            # Se intenta insertar el DataFrame en la base de datos.
                            try:
                                tablaProduccion.to_sql(f"maquina_{numMaquina}_produccion", engine, if_exists='append', index = False)
                                print(f"Datos de la tabla maquina_{numMaquina}_produccion actualizados correctamente.")
                            except Exception as e:
                                print(f"Error al insertar los datos en la tabla maquina_{numMaquina}_produccion", estado)
                                x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al insertar los datos en la tabla maquina_{numMaquina}_produccion.\n')
                                insertarBD()
            else:
                # Para que en caso de que exista una ultima fecha.
                ultimaFechaDateTime = datetime.datetime.strptime(ultimaFecha[0][0].strftime("%Y-%m-%d"), '%Y-%m-%d')
                splitNombreTabla = cadaTabla[0].split('_')
                numMaquina = splitNombreTabla[1]
                # Se recorren todos los archivos CSV.
                for cadaCSV in os.listdir('./archivos'):
                    splitNombre = cadaCSV.split('_')
                    fecha = splitNombre[2].split('.')[0]
                    # Se parsea la fecha a DateTime.
                    fechaDateTime = datetime.datetime.strptime(fecha, '%Y-%m-%d')
                    # Para eliminar los 0 del inicio de la cadena.
                    numMaquinaCSV = splitNombre[1].lstrip('0')
                    # Para comprobar que la fecha del CSV es mayor a la ultima fecha de la base de datos y que los numeros de las maquinas son los mismos.
                    if fechaDateTime > ultimaFechaDateTime and numMaquina == numMaquinaCSV:
                        csv = pd.read_csv(f"./archivos/{cadaCSV}")
                        if not csv.empty:
                            # Para leer los datos que se van a insertar en la tabla "_estado".
                            try:
                                # IMPORTANTE poner los espacios delabte de cada columna ya que asi estan en el CSV.
                                tablaEstado = csv[["-", " PORCENTAJE NOK" ," TOTALES", " ERROR PROCESADO"]]
                                tablaEstado.rename({'-': 'TIPO'}, axis=1, inplace=True)
                                arrayID = []
                                arrayFecha = []
                                arrayEstado = []
                                # Se recorren todas las filas del CSV y se inserte la ID, la fecha y estado en un array
                                # que despues se insertara al DataFrame.
                                for i in range(0, len(tablaEstado.index)):
                                    numPorcentajeNOK = tablaEstado.iloc[i][" PORCENTAJE NOK"]
                                    estado = 1
                                    if numPorcentajeNOK >= 50:
                                        estado = 0
                                    elif numPorcentajeNOK >= 30:
                                        estado = 2
                                    arrayID.append(0)
                                    arrayFecha.append(fecha)
                                    arrayEstado.append(estado)
                                # Para insertar los datos del array en el DataFrame.
                                tablaEstado.insert(0, "id", arrayID)
                                tablaEstado.insert(1, "FECHA", arrayFecha)
                                tablaEstado.insert(3, "ESTADO", arrayEstado)
                                # Para eliminar los espacios en blanco de los nombres de las columnas.
                                tablaEstado.columns = tablaEstado.columns.str.strip()
                            except Exception as e:
                                print(f"Error al generar el DataFrame de la tabla maquina_{numMaquina}_estado", e)
                                x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al generar el DataFrame de la tabla maquina_{numMaquina}_estado.\n')
                                insertarBD()
                            # Se intenta insertar el DataFrame en la base de datos.
                            try:
                                tablaEstado.to_sql(f"maquina_{numMaquina}_estado", engine, if_exists='append', index = False)
                                print(f"Datos de la tabla maquina_{numMaquina}_estado actualizados correctamente.")
                            except Exception as e:
                                print(f"Error al insertar los datos en la tabla maquina_{numMaquina}_estado", e)
                                x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al insertar los datos en la tabla maquina_{numMaquina}_estado.\n')
                                insertarBD()

                            # Para leer los datos que se va a insertar en la tabla "_produccion".
                            try:
                                # IMPORTANTE poner los espacios delabte de cada columna ya que asi estan en el CSV.
                                tablaProduccion = csv[["-", " TOTALES OK", " TOTALES NOK", " PORCENTAJE NOK", " ETIQUETA SUP NOK", " ETIQUETA INF NOK", " LOTE NOK", " COCCION NOK", " GRIETAS NOK", " BOCADOS NOK", " REBORDES NOK", " AGUJERO NOK", " SELLADO NOK", " OBJ EXT NOK"]]
                                tablaProduccion.rename({"-": "TIPO"}, axis=1, inplace=True)
                                arrayID = []
                                arrayFecha = []
                                arrayEstado = []
                                # Se recorren todas las filas del CSV y se inserte la ID, la fecha y estado en un array
                                # que despues se insertara al DataFrame.
                                for i in range(0, len(tablaProduccion.index)):
                                    porcentaje_nok = tablaProduccion.iloc[i][" PORCENTAJE NOK"]
                                    estado = 1
                                    if porcentaje_nok >= 50:
                                        estado = 0
                                    elif porcentaje_nok >= 30:
                                        estado = 2
                                    arrayID.append(0)
                                    arrayFecha.append(fecha)
                                    arrayEstado.append(estado)
                                # Para insertar los datos del array en el DataFrame.
                                tablaProduccion.insert(0, "id", arrayID)
                                tablaProduccion.insert(1, "FECHA", arrayFecha)
                                tablaProduccion.insert(2, "ESTADO", arrayEstado)
                                # Para eliminar los espacios en blanco de los nombres de las columnas.
                                tablaProduccion.columns = tablaProduccion.columns.str.strip()
                            except Exception as e:
                                print(f"Error al generar el DataFrame de la tabla maquina_{numMaquina}_produccion", e)
                                x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al generar el DataFrame de la tabla maquina_{numMaquina}_produccion.\n')
                                insertarBD()
                            try:
                                tablaProduccion.to_sql(f"maquina_{numMaquina}_produccion", engine, if_exists='append', index = False)
                                print(f"Datos de la tabla maquina_{numMaquina}_produccion actualizados correctamente.")
                            except Exception as e:
                                print(f"Error al insertar los datos en la tabla maquina_{numMaquina}_produccion", estado)
                                x.write(f'->{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}: Error al insertar los datos en la tabla maquina_{numMaquina}_produccion.\n')
                                insertarBD()


# Funcion para que se ejecute el main para descargar los CSVs e insertar los datos en la base de datos cada hora.
def setInterval(func,time):
    e = threading.Event()
    while not e.wait(time):
        func()


# Para ejecutar el main
if __name__ == "__main__":
    setInterval(main, 3600)
    exit()