from typing import Dict, Any
import time
import traceback as traceback_lib
import importlib

from config import Config as cfg
from app.main.engine.util.Timezone import Timezone
from app.main.engine.util.Utils import Utils
from app.main.engine.enum.StatusEnum import StatusEnum
from app.main.engine.enum.CodeResponseEnum import CodeResponseEnum
from app.main.engine.database.Database import Database


class Process:
    def __init__(self, app, descriptor: Dict[str, Any]) -> None:
        self.app = app
        self.descriptor = descriptor

        self.timezone = Timezone(app)
        self.utils = Utils(app)

    def run(self) -> Dict[str, Any]:
        status, status_description = StatusEnum.OK, ""
        try:
            # Obteniendo la conexión a la BD
            db_params = cfg.db_params
            source = Database(self.app, db_params)
            db_session = source.get_session()

            # Obteniendo el nombre del script
            script_name = source.get_action_by_identifier(self.descriptor["idScript"], db_session)
            if isinstance(script_name, type(None)):
                raise ModuleNotFoundError(f"Error al descargar script desde volumen")

            path_script = "scripts/"+script_name
            module = path_script[:-3].replace("/", ".")

            import_module = importlib.import_module(module)
            importlib.reload(import_module)
            globals().update(importlib.import_module(module).__dict__)

            # Parsea los parámetros de entrada
            relation = relation_classname_identifier[self.descriptor["idScript"]]
            obj_script = globals()[relation](self.app)

            obj_script.parser(self.descriptor)

            # Iniciando process
            self.app.logger.info(f"Iniciando procesamiento de script")
            obj_script.process(source)

            # Guardando resultado
            self.app.logger.info(f"Generado y guardando resultado")
            response = obj_script.response()
            # response.show()
            result = self.utils.create_result(response, self.descriptor)
            save = self.utils.save_result(result, self.descriptor, db_session)
            if save["status"] == StatusEnum.ERROR.name:
                raise InterruptedError(save["message"])
        except IndexError as e:
            self.app.logger.error(f"Error extrayendo insumos. Vacío. Error: {e}")
            status, status_description = CodeResponseEnum.EMPTY_DATASET, str(e)
        except InterruptedError as e:
            self.app.logger.error(f"Error guardando resultados en la Base de Datos. Error: {e}")
            status, status_description = CodeResponseEnum.OUTPUT_ERROR, str(e)
        except BufferError as e:
            self.app.logger.error(f"Error obteniendo la data. Error: {e}")
            status, status_description = CodeResponseEnum.BD_INPUT_ERROR, str(e)
        except RuntimeError as e:
            self.app.logger.error(f"Error en la ejecución del script {self.descriptor['action_module_script']}. Error: {e}")
            status, status_description = CodeResponseEnum.SCRIPT_EXECUTION_ERROR, str(e)
        except ModuleNotFoundError as e:
            self.app.logger.error(f"Error obteniendo script {self.descriptor['action_module_script']}. Error: {e}")
            status, status_description = CodeResponseEnum.SCRIPT_ERROR, str(e)
        except ReferenceError as e:
            self.app.logger.error(f"Error validando parámetros del descriptor. {e}")
            status, status_description = CodeResponseEnum.PARAMETERS_ERROR, str(e)
        except Exception as e:
            traceback_lib.print_exc()
            self.app.logger.error(f"Error procesando engine. {e}")
            status, status_description = StatusEnum.ERROR, str(e)
        finally:
            return self.utils.create_response(status, status_description)
