#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Jan 24 10:27:19 2025

@author: pablofernandez
"""

import json

# --- RUTAS CONFIGURABLES ---
import os
import sys

from logic.utils import resource_path


# BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# db_path = os.path.join(BASE_DIR, '..', 'data', 'Productos_datos_5.json')
# hazard_mapping_path = os.path.join(BASE_DIR, '..', 'data', 'hazard_to_tox_mapping_extended.json')

db_path = resource_path("data/Productos_datos_5.json")
hazard_mapping_path = resource_path("data/hazard_to_tox_mapping_extended.json")

# --- FUNCIONES GENERALES ---

# Cargar base de datos de productos
def load_database(path):
    with open(path, 'r', encoding='utf-8') as file:
        return json.load(file)

# Cargar mapeo de H-statements
def load_hazard_mapping(path):
    with open(path, 'r', encoding='utf-8') as file:
        return json.load(file)

# Obtener datos del producto según el nombre comercial
def get_product_data(product_name, database):
    product = next((item for item in database if item["Nombre_comercial"].lower() == product_name.lower()), None)
    if not product:
        raise ValueError(f"Producto '{product_name}' no encontrado en la base de datos.")
    return product

# Determinar valores de variables principales con sus condiciones
def determine_values(product, **inputs):
    """
   Determina los valores principales según las condiciones definidas.
   :param product: Diccionario con los datos del producto.
   :param inputs: Diccionario con los valores de entrada del usuario.
   :return: Tupla con los valores calculados.
   """
   # Validar los valores de entrada
  # Campos que deben ser numéricos
    numeric_keys = [
        "dose_input", "load_input", "surf_input", "bar_input",
        "alt_input", "sep_input", "skill_input" # "earl_input", "freq_input"
    ]

    # Validar valores de entrada
    print("\n🔎 Depuración de valores en determine_values():")
    for key, value in inputs.items():
        if key in numeric_keys and not isinstance(value, (int, float)):
            raise ValueError(f"Error: '{key}' debe ser un número, pero tiene el valor '{value}' y tipo {type(value)}")

    # DOSE, CONC, COMP del producto
    dose_max = float(product["Dosis"])  # Dosis máxima autorizada según la base de datos
    dose_value = 0.3 if inputs["dose_input"] < dose_max else (1 if inputs["dose_input"] == dose_max else (lambda: (_ for _ in ()).throw(ValueError("La dosificación supera la dosis máxima autorizada.")))())  # Dosis < dosis máxima autorizada (kg/ha) (valor = 0.3);  Dosis = dosis máxima autorizada (kg/ha) (valor = 1). Si es superior, Dosis = dosis máxima y mensaje de advertencia de que está aplicando mayor dosis de la autorizada
    conc = float(product["Concentracion"])  # Convertir a número
    conc_value = 0.1 if conc < 1 else (0.3 if 1 <= conc <= 90 else 1)  # Concentración < 1% (valor = 0.1); 1% ≤ Conc ≤ 90% (valor = 0.3); Conc > 90% (valor = 1)
    comp_mapping = {"bolsas solubles": 0.3, "gránulos": 1, "líquido": 1, "polvo": 3}  # Bolsas solubles (valor = 0.3); Gránulos o líquido (valor = 1); Polvo (valor = 3)
    comp_value = comp_mapping.get(product["Composicion"].lower(), 1)
    
    # Depuración: Imprimir valores clave
    print(f"📌 DOSE: {dose_value}, CONC: {conc_value}, COMP: {comp_value}")

    # LOAD
    load_value = 0.3 if inputs["load_input"] < 3 else (1 if 3 <= inputs["load_input"] <= 5 else 3)  # Menos de 3 cargas (valor = 0.3); De 3 a 5 cargas (valor = 1); Más de 5 cargas (valor = 3)
    # LUGAR
    lugar_value = (
    10 if inputs["ubicacion_input"] == "Interior" and inputs["tamaño_recinto_input"] == "Pequeño (inferior a 10 m²)" and inputs["ventilacion_input"] == "Ninguna" else
    3 if inputs["ubicacion_input"] == "Interior" and inputs["tamaño_recinto_input"] == "Pequeño (inferior a 10 m²)" and inputs["ventilacion_input"] == "Natural" else
    0.3 if inputs["ubicacion_input"] == "Interior"  and inputs["tamaño_recinto_input"] == "Pequeño (inferior a 10 m²)" and inputs["ventilacion_input"] == "Forzada" else
    7 if inputs["ubicacion_input"] == "Interior"  and inputs["tamaño_recinto_input"] == "Grande (superior a 10 m²)" and inputs["ventilacion_input"] == "Ninguna" else
    1.75 if inputs["ubicacion_input"] == "Interior" and inputs["tamaño_recinto_input"] == "Grande (superior a 10 m²)" and inputs["ventilacion_input"] == "Natural" else
    0.3 if inputs["ubicacion_input"] == "Interior" and inputs["tamaño_recinto_input"] == "Grande (superior a 10 m²)" and inputs["ventilacion_input"] == "Forzada" else
    1 if inputs["ubicacion_input"] == "Exterior" else
    ValueError("Entrada no válida para 'tamaño_recinto_input', 'ventilacion_input' o 'ubicacion_input'.")
)
    # SURF
    surf_value = 0.3 if inputs["surf_input"] < 1 else (1 if inputs["surf_input"] == 1 else 3)  # Superficie < 1 ha (valor = 0.3); Superficie = 1 ha (valor = 1); Superficie > 1 ha (valor = 3)
    # BAR
    bar_value = 0.3 if inputs["bar_input"] < 18 else (1 if inputs["bar_input"] == 18 else 7)  # Presión < 18 bar (valor = 0.3); Presión = 18 bar (valor = 1); Presión > 18 bar (valor = 7)
    # ALT
    alt_value = 0.3 if inputs["alt_input"] < 0.8 else (1 if 0.8 <= inputs["alt_input"] <= 1.5 else 3)  # Altura < 0.8 m (valor = 0.3); 0.8 ≤ Altura ≤ 1.5 m (valor = 1); Altura > 1.5 m (valor = 3)
    # SEP
    sep_value = 0.3 if inputs["sep_input"] > 2.4 else (1 if 1.25 <= inputs["sep_input"] <= 2.4 else 3)  # Separación > 2.4 m (valor = 0.3); 1.25 ≤ Separación < 2.4 m (valor = 1); Separación < 1.25 m (valor = 3)
    # EQUIP
    equip_mapping = {
        "vehículo autopropulsado": {"value": 0.1, "velocity": 2, "factor": 2},
        "carro manual": {"value": 0.3, "velocity": 1, "factor": 2},
        "lanza pulverizadora": {"value": 3, "velocity": 0.6, "factor": 1},
        "pistola pulverizadora": {"value": 1, "velocity": 0.6, "factor": 1}
    }
    equip_info = equip_mapping.get(inputs["equip_input"].lower(), {"value": 1, "velocity": 1, "factor": 1})
    equip_value = equip_info["value"]  # Valor del equipo
    vel_equip = equip_info["velocity"]  # Velocidad del equipo (aplicado en el cálculo de tiempo de aplicación t_A)
    factor = equip_info["factor"]  # Factor aplicado en el cálculo de tiempo de aplicación t_A

    # SENT
    sent_value = (
        {"hacia adelante": 1, "hacia atrás": 0.3}.get(inputs["sent_input"].lower(), 1)  # Sentido de la aplicación. Hacia adelante (valor = 1); Hacia atrás (valor = 0.3)
        if inputs["equip_input"].lower() in ["pistola pulverizadora", "lanza pulverizadora"]
        else 1
        )
    # FUGAS
    fugas_mapping = {"sí": 10, "no": 1} # Fugas en el equipo (valor = 10); Sin fugas (valor = 1)
    fugas_value = fugas_mapping[inputs["fugas_input"].lower()]
    # VENT
    vent_mapping = {
        "sin ventilación": 3,
        "lateral": 1,
        # "cenital": 2,
        "lateral y cenital": 0.3
    }
    vent_value = vent_mapping[inputs["vent_input"].lower()]  # Sin ventilación (valor = 3); Lateral (valor = 1); Lateral y cenital (valor = 0.3)
    # REPAIR
    repair_mapping = {"persona diferente": 0, "mismo operario": 10} # Realizadas por persona diferente (valor = 0); Realizadas por el mismo operario (valor = 10)
    repair_value = repair_mapping[inputs["repair_input"].lower()]
    # EARL
    earl_mapping = {"sí": 0, "no": 1.5} # Sí respeta plazo de reentrada (valor = 0); No respeta plazo de reentrada (valor = 1.5)
    earl_value = earl_mapping[inputs["earl_input"].lower()]
    # PPE
    ppe_mapping = {"buen estado": 1, "mal estado": 3} # Equipo en buen estado (valor = 1); Equipo en mal estado (valor = 3)
    ppe_value = ppe_mapping[inputs["ppe_input"].lower()]
    # SKILL
    skill_value = 3 if inputs["skill_input"] < 1 else (1 if 1 <= inputs["skill_input"] <= 5 else 0.3)  # Experiencia 1-5 años (valor = 1); Más de 5 años (valor = 0.3); Menos de 1 año (valor = 3)
    # # FREQ (No se va a considerar en el modelo finalmente)
    # freq_value = 0.3 if inputs["freq_input"] < 5 else (1 if 5 <= inputs["freq_input"] <= 10 else (3 if 11 <= inputs["freq_input"] <= 20 else 7))  # Nº días al año dedicados a aplicación fitosanitarios < 5 días (valor = 0.3); 5-10 días (valor = 1); 11-20 días (valor = 3); > 20 días (valor = 7)

    return (dose_value, conc_value, comp_value, load_value, lugar_value, surf_value, 
            bar_value, alt_value, sep_value, equip_value, vel_equip, factor, sent_value, 
            fugas_value, vent_value, repair_value, earl_value, ppe_value, skill_value) #, freq_value)


# Cálculo de MIX, APPL, y REENTRY
def calculate_mix(load, conc, comp, lugar):
    return (load * conc) * comp * lugar

def calculate_appl(dose, surf, bar, alt, sep, equip, sent, fugas, vent):
    return (dose * surf * bar * alt * sep) * (equip * sent) + fugas + vent

def calculate_reentry(dose, alt, sep, earl):
    return dose * alt * sep * earl

# Cálculo de tiempos
def calculate_times(surf_input, sep_input, vel_equip, factor, t_RE_hours_input, t_T=8):  # Jornada laboral estándar en horas. Por defecto 8 horas.
    t_M_hours = 0.2
    t_M = t_M_hours / t_T  # Porcentaje de tiempo dedicado a la tarea de mezcla/carga
    t_A_hours = (surf_input * 10000 / sep_input) * factor / vel_equip / 3600
    t_A = t_A_hours / t_T  # Porcentaje de tiempo dedicado a la tarea de aplicación del producto
    # Validar el tiempo de reentrada
    if t_RE_hours_input < 0 or t_RE_hours_input > t_T:
        raise ValueError("El tiempo de reentrada debe estar entre 0 y 8 horas.")
    t_RE = t_RE_hours_input / t_T  # Porcentaje de tiempo dedicado a la tareas realizadas durante la re-entrada al invernadero. t_RE_hours_input: dato introducido por el usuario (en horas)
    return t_A, t_M, t_RE

# Cálculo de I_exp
def calculate_i_exp(mix, t_M, appl, t_A, repair, reentry, t_RE, skill, ppe): #, freq):
    return ((mix * t_M + appl * t_A + repair + reentry * t_RE) * skill * ppe) # * freq

# Determinar nivel de exposición basado en I_exp
def determine_exposure_level(i_exp):
    if 1 <= i_exp <= 10:
        return {"level": 1, "description": "Nivel 1. Exposición despreciable o  muy baja"}
    elif 11 <= i_exp <= 50:
        return {"level": 2, "description": "Nivel 2. Exposición baja dentro de límites aceptables"}
    elif 51 <= i_exp <= 300:
        return {"level": 3, "description": "Nivel 3. Exposición moderada con potencial impacto"}
    elif 301 <= i_exp <= 1000:
        return {"level": 4, "description": "Nivel 4. Exposición alta, posible riesgo significativo"}
    else:
        return {"level": 5, "description": "Nivel 5. Exposición extrema, medidas urgentes necesarias"}  # I_exp > 15000

# Determinación de I_tox
def determine_correct_i_tox(h_stat, hazard_to_tox_mapping):
    if not h_stat or h_stat.strip() == "1":
        return 1
    hazards = h_stat.split(",")
    return max(hazard_to_tox_mapping.get(h.strip(), {"I_tox": 1})['I_tox'] for h in hazards)

# Cálculo de Re y niveles de riesgo
def calculate_re(i_exp, i_tox):
    re = i_exp * i_tox
    if 1 <= re <= 50:
        risk_level = "<b>Nivel I.</b> Riesgo de exposición aceptable sin necesidad de medidas adicionales."
        mensaje = ""
    elif 51 <= re <= 100:
        risk_level = "<b>Nivel II.</b> Riesgo de exposición bajo, puede requerir monitoreo o medidas preventivas leves."
        mensaje = (
            "<b style='display:inline-block; margin-bottom: 16px;'>Medidas preventivas:</b>"
            "<ul style='list-style-type: none; padding-left: 0;'>"
            "<li>◦ Actualizar la información y las actividades de capacitación.<li>"
            "<li>◦ Seguir las condiciones de uso recogidas en la etiqueta.<li>"
        )
    elif 101 <= re <= 1000:
        risk_level = "<b>Nivel III.</b> Riesgo de exposición moderado, se recomienda control y medidas correctivas."
        mensaje = (
            "<b style='display:inline-block; margin-bottom: 16px;'>Medidas preventivas:</b>"
            "<ul style='list-style-type: none; padding-left: 0;'>"
            "<li>◦ Actualizar la información y las actividades de capacitación.<li>"
            "<li>◦ Verificar la adecuación y el estado del equipo de protección personal.<li>"
            "<li>◦ Verificar la adecuación de los fitosanitarios utilizados.<li>"
        )
    elif 1001 <= re <= 5000:
        risk_level = "<b>Nivel IV.</b> Riesgo de exposición alto, se requiere intervención activa y reducción de riesgo."
        mensaje = (
            "<b style='display:inline-block; margin-bottom: 16px;'>Medidas preventivas:</b>"
            "<ul style='list-style-type: none; padding-left: 0;'>"
            "<li>◦ Actualizar la información y las actividades de capacitación.<li>"
            "<li>◦ Verificar la adecuación y el estado del equipo de protección personal.<li>"
            "<li>◦ Verificar la adecuación de los fitosanitarios utilizados y la posibilidad de seleccionar productos menos peligrosos, proporcionando un monitoreo biológico/ambiental específico.<li>"
            "<li>◦ Verificar la adecuación y el estado del equipo de aplicación de fitosanitarios y sus operaciones de mantenimiento.<li>"
            "<li>◦ Actualizar los procedimientos e instrucciones operativas y llevar a cabo una nueva evaluación de riesgos.<li>"
        )
    else:
        risk_level = "<b>Nivel V.</b> Riesgo de exposición inaceptable, se deben implementar medidas inmediatas."
        mensaje = (
            "<b style='display:inline-block; margin-bottom: 16px;'>Medidas preventivas:</b>"
            "<ul style='list-style-type: none; padding-left: 0;'>"
            "<li>◦ Verificar la adecuación de los fitosanitarios utilizados y considerar productos menos peligrosos.<li>"
            "<li>◦ Implementar un monitoreo biológico/ambiental específico y vigilancia de la salud de los operarios.<li>"
            "<li>◦ Verificar la selección del equipo de protección personal y su estado, así como los procedimientos operativos.<li>"
            "<li>◦ Verificar la adecuación y el estado del equipo de aplicación y su mantenimiento.<li>"
            "<li>◦ Realizar una nueva evaluación de riesgo.<li>"
        )
    return re, risk_level, mensaje

# Calcular valores principales y resultados intermedios
def calculate_risk(input_data):
    
    try:
        # Cargar bases de datos
        database = load_database(db_path)
        hazard_mapping = load_hazard_mapping(hazard_mapping_path)

        # Obtener datos del producto
        product = get_product_data(input_data["product_name_input"], database)
        
        # Validar que el producto tenga los datos necesarios
        if not product.get("Dosis") or not product.get("Concentracion"):
            raise ValueError(f"El producto '{product['Nombre_comercial']}' no tiene información de dosis o concentración.")

        # Determinar valores principales
        values = determine_values(product=product, **input_data) 

        # Asignar valores calculados
        (dose_value, conc_value, comp_value, load_value, lugar_value, surf_value, 
         bar_value, alt_value, sep_value, equip_value, vel_equip, factor, sent_value, 
         fugas_value, vent_value, repair_value, earl_value, ppe_value, skill_value) = values #, freq_value) = values
        
        # Comprobación de la dosis y concentración extraídas de la base de datos de los productos SE PUEDE ELIMINAR ESTA LÍNEA
        conc = float(product["Concentracion"])  # Convertir a número
        dose_max = float(product["Dosis"])  # Dosis máxima autorizada según la base de datos

        print("\n📋 Depuración antes del cálculo en calculate_risk:")
        variables = {
            "DOSE": dose_value, "CONC": conc_value, "COMP": comp_value, "LOAD": load_value, 
            "LUGAR": lugar_value, "SURF": surf_value, "BAR": bar_value, "ALT": alt_value, 
            "SEP": sep_value, "EQUIP": equip_value, "VEL_EQUIP": vel_equip, "FACTOR": factor,
            "SENT": sent_value, "FUGAS": fugas_value, "VENT": vent_value, "REPAIR": repair_value, 
            "EARL": earl_value, "PPE": ppe_value, "SKILL": skill_value
        }

        for key, value in variables.items():
            print(f"🔹 {key}: {value} ({type(value)})")

        # Cálculos intermedios
        mix = calculate_mix(load_value, conc_value, comp_value, lugar_value)
        appl = calculate_appl(dose_value, surf_value, bar_value, alt_value, sep_value, equip_value, sent_value, fugas_value, vent_value)
        reentry = calculate_reentry(dose_value, alt_value, sep_value, earl_value)
        t_A, t_M, t_RE = calculate_times(float(input_data["surf_input"]), float(input_data["sep_input"]), vel_equip, factor, float(input_data["t_RE_hours_input"]))

        # Cálculo de I_exp
        i_exp = calculate_i_exp(mix, t_M, appl, t_A, repair_value, reentry, t_RE, skill_value, ppe_value) #, freq_value)

        # Calcular nivel de exposición basado en I_exp
        exposure_level = determine_exposure_level(i_exp)

        # Cálculo de I_tox
        i_tox = determine_correct_i_tox(product["H_stat"], hazard_mapping)

        # Cálculo de Re
        re, risk_level, mensaje = calculate_re(i_exp, i_tox)

        # Devolver resultados
        return {
            "Producto": input_data["product_name_input"],
            "DOSE_max": dose_max,
            "DOSE_value": dose_value,
            "CONC": conc,
            "CONC_value": conc_value,
            "COMP_producto": product["Composicion"],
            "COMP_value": comp_value,
            "MIX": round(mix,3),
            "APPL": appl,
            "REENTRY": round(reentry,2),
            "REPAIR": repair_value,
            "I_exp": round(i_exp,2),
            "Nivel de Exposición": exposure_level["level"],
            "Descripción de Exposición": exposure_level["description"],
            "I_tox": i_tox,
            "H_stat": product["H_stat"],
            "Re": round(re,1),
            "Nivel de Riesgo": risk_level,
            "Mensaje asociado": mensaje
        }
    except Exception as e:
       import traceback
       return {"error": str(e), "details": traceback.format_exc()}

