import asyncio
import sys
import signal
import psutil
import os
import traceback

if sys.platform == 'linux':
    sys.path.append(os.getcwd() + "/funciones")
else:
    sys.path.append(os.getcwd() + "\\funciones")

from dotenv import load_dotenv

load_dotenv()
from funciones.principales import monitorear_chat, monitorear_mensajes, enviar_pendientes_a_usuarios, \
    obtener_pendientes_y_enviar, pedir_mensajes

from funciones.asincrono import abrir_navegador, apagar

from funciones.utiles import parsear_bytes,deshabilitar_prints
from bd.bd import bootstrap_bd, verificar_registros

import selenium.common.exceptions as seleniumexc

from datetime import datetime, timedelta

from loguru import logger

logger.add("atwsp.log", enqueue=True)

MINUTOS_LOGUEO = 2



import time




async def main():
    # deshabilitar_prints()

    print("iniciando main")
    # Inicializamos la base de datos, creando las tablas si no existen
    try:
        await bootstrap_bd()
    except:
        print("falla ini")
    try:
        verificar_registros()
    except:
        print("falla intento de backup")
    # Conectamos con el driver de selenium, abriendo asi el navegador con whatsapp
    driver = abrir_navegador()
    ultimo_logeo = await obtener_fechahora_actual()  # Inicializamos el horario de logeo
    loop = asyncio.get_running_loop()
    print("ejecuto loop")
    # CODIGO DE SEÑALES COMENTADO PORQUE EN WINDOWS NO FUNCIONA
    # # Definimos las señales ante las cuales el programa debe apagarse
    # signals = (signal.SIGHUP, signal.SIGTERM, signal.SIGINT)
    #
    # for s in signals:  # Agregamos las señales al loop
    #     # Nuestro handler para las señales definidas arriba, es la corutina 'apagar', la cual se encargara de
    #     # finalizar la sesion del webdriver (NO LA VENTANA, LA SESION), y de cancelar todas las corutinas pendientes
    #     # liberando asi los recursos que estaban ocupando.
    #     loop.add_signal_handler(
    #         s, lambda s=s: asyncio.create_task(apagar(loop, driver)))

    # print("antes del while")
    while True:

        await asyncio.sleep(1)

        # Con este try verificamos si el webdriver se cerro, en caso de estarlo, enviamos una señal para
        # que se cancelen todas las tareas, liberando asi los recursos que estan ocupando
        try:
            _ = driver.window_handles
        except (seleniumexc.InvalidSessionIdException,
                seleniumexc.WebDriverException):
            print("error")
            logger.warning("Finalizando el programa, se cerro el webdriver")
            # os.kill(os.getpid(), signal.SIGTERM)
            await apagar(loop, driver)
            quit()

        respuestas = []
        try:

            rta_obtener_para_enviar = await pedir_mensajes()  # pide msj a atapi
            # print("\nvolvio pedido. paso:", time.time() - ahora)
            respuestas.append(rta_obtener_para_enviar)

            rta_monitor = monitorear_chat(driver)  # lee chat ventana abierta
            # print("volvio ventana actual. paso:", time.time() - ahora)
            print(rta_monitor)
            respuestas.append(rta_monitor)

            # NO PONER ESTE ANTES DE MONITOREAR CHAT,
            # XQ SI LLEGA UN MENSAJE EN LA VENTANA ABIERTA AL MISMO TIEMPO QUE UN MENSAJE DE OTRO USUARIO.
            # EL DE LA VENTANA ABIERTA NO LLEGA A TOMARLO

            rta_monitor_msjs = monitorear_mensajes(driver)  # verifica si hay mensajes nuevos en panel lateral
            # print("volvio monitoreo lateral. paso:", time.time() - ahora)
            respuestas.append(rta_monitor_msjs)


            rta_enviar_atapis = await obtener_pendientes_y_enviar()  # envia msj a atapis
            # print("volvio enviar msj a atapis. paso:", time.time() - ahora)
            respuestas.append(rta_enviar_atapis)


            rta_enviar_usuarios = enviar_pendientes_a_usuarios(driver)  # envia mjs a usuarios
            # print("volvio envio usuarios. paso:", time.time() - ahora)
            respuestas.append(rta_enviar_usuarios)

        except Exception as error:
            print(f"{error}")

        for rta in respuestas:
            # Si alguna de las corutinas levanto una excepcion o devolvio respuesta False
            if isinstance(rta, Exception):
                logger.warning("Una de las corutinas levanto excepcion: {}", rta)
                try:
                    raise rta
                except:
                    print(traceback.format_exc())

            if isinstance(rta, dict) and rta['Estado'] is False:
                logger.warning("Una de las corutinas devolvio estado False: {}", rta)

        # Logearemos el hardware cada tantos minutos
        if await debe_loguear_recursos(ultimo_logeo, MINUTOS_LOGUEO):
            ram = await obtener_estado_memoria_virtual()

            await loguear_recursos(ram)

            ultimo_logeo = await obtener_fechahora_actual()


async def debe_loguear_recursos(ultimo_logeo, minutos):
    return await obtener_fechahora_actual() - timedelta(minutes=minutos) >= ultimo_logeo


async def obtener_fechahora_actual():
    return datetime.now()


async def loguear_recursos(ram):
    logger.info("Uso del CPU: {}% | RAM libre: {} | RAM usada: {}",
                psutil.cpu_percent(), parsear_bytes(ram.available), parsear_bytes(ram.used))


async def obtener_estado_memoria_virtual():
    return psutil.virtual_memory()


if __name__ == "__main__":
    asyncio.run(main(), debug=False)
