# web/scripts/script7_optimizador.py

import os
from scripts.ens_controls import ENSControlLoader

# --------------------------------------------------
# 1) Definición de constantes (horas y precios base)
# --------------------------------------------------

PRECIOS = {
    # Formación de usuarios y de técnicos
    "formacion_usuario_hora":   100,   # €/hora consultor para formación de usuarios
    "formacion_tecnico_hora":   120,   # €/hora consultor para formación técnica

    # Auditoría externa
    "auditoria_hora":           150,   # €/hora auditor Senior

    # Políticas / procedimientos
    "politica_hora":            90,    # €/hora consultor para redactar políticas

    # Herramientas técnicas (coste único)
    "antivirus_por_usuario":    30,    # €/usuario para antivirus corporativo (licencia anual)
    "firewall_perimetral":      3000,  # € coste único de firewall perimetral + instalación
    "siem_inicial":             2000,  # € coste único de SIEM / plataforma de logs (licencia inicial)

    # Oficial de seguridad (coste anual “20% de un salario medio”)
    "oficial_seguridad_anual":  6000   # € coste anual fijo (20% de un sueldo aproximado de 30 000 €/año)
}

HORAS = {
    "formacion_por_empleado":   2,   # horas de formación básica por empleado
    "formacion_por_servidor":   8,   # horas de formación avanzada para cada técnico/servidor
    "politica_por_control":     5,   # horas estimadas para redactar cada política/procedimiento ENS
    "auditoria_por_sede":       20   # horas de auditoría anual mínima por cada sede
}


# --------------------------------------------------
# 2) Función principal de cálculo
# --------------------------------------------------

def calcular_estimacion(empleados: int,
                        tecnicos: int,
                        sedes: int,
                        madurez: str,
                        presupuesto: float,
                        estado_controles: dict) -> dict:
    """
    Calcula, a partir de:
      - empleados (int)
      - tecnicos (int)
      - sedes (int)
      - madurez ('bajo' / 'medio' / 'alto')
      - presupuesto (float, €)
      - estado_controles: { 'Código ENS': True/False }, obtenidos de session['evaluacion_controles']

    Devuelve un diccionario con:
      {
        "partidas": {
            "formacion_usuarios":    { "horas": X, "coste": Y, "texto": "..."},
            "formacion_tecnicos":    { "horas": X, "coste": Y, "texto": "..."},
            "politicas":             { "horas": H_p, "coste": C_p, "texto": "..."},
            "auditoria_externa":     { "horas": H_a, "coste": C_a, "texto": "..."},
            "antivirus":             { "horas": 0, "coste": C_av, "texto": "..."},
            "firewall":              { "horas": 0, "coste": C_fw, "texto": "..."},
            "siem":                  { "horas": 0, "coste": C_si, "texto": "..."},
            "plan_continuidad":      { "horas": H_c, "coste": C_c, "texto": "..."},
            "gestion_incidentes":    { "horas": H_i, "coste": C_i, "texto": "..."},
            "oficial_seguridad":     { "horas": 0, "coste": C_of, "texto": "..."}
        },
        "totales": {
            "horas_totales":           total_horas,
            "coste_total_inicial":     total_coste_inicial,
            "coste_recurrente_anual":  coste_recurrente_anual
        },
        "plan_prioridades": [
            { "concepto": "formacion_usuarios", "coste": X1, "acumulado": A1, "texto": "..." },
            { "concepto": "antivirus",        "coste": X2, "acumulado": A2, "texto": "..." },
            …
        ]
      }
    """

    resultado = {
        "partidas": {},
        "totales": {},
        "plan_prioridades": []
    }

    # ————————————————————————————————————————————
    # 1) Formación de USUARIOS
    # ————————————————————————————————————————————
    horas_usuarios = empleados * HORAS["formacion_por_empleado"]
    coste_usuarios = horas_usuarios * PRECIOS["formacion_usuario_hora"]
    texto_usuarios = (
        f"{empleados} empleados × {HORAS['formacion_por_empleado']} h = {horas_usuarios} h; "
        f"a €{PRECIOS['formacion_usuario_hora']}/h ⇒ €{coste_usuarios:.2f}."
    )
    resultado["partidas"]["formacion_usuarios"] = {
        "horas": horas_usuarios,
        "coste": round(coste_usuarios, 2),
        "texto": texto_usuarios
    }

    # ————————————————————————————————————————————
    # 2) Formación de TÉCNICOS / ADMINISTRADORES
    # ————————————————————————————————————————————
    # Si la PYME no tiene técnicos, asumimos 1 técnico externo mínimo
    num_tecnicos = tecnicos if tecnicos > 0 else 1
    horas_tecnicos = num_tecnicos * HORAS["formacion_por_servidor"]
    coste_tecnicos = horas_tecnicos * PRECIOS["formacion_tecnico_hora"]
    texto_tecnicos = (
        f"{num_tecnicos} técnicos × {HORAS['formacion_por_servidor']} h = {horas_tecnicos} h; "
        f"a €{PRECIOS['formacion_tecnico_hora']}/h ⇒ €{coste_tecnicos:.2f}."
    )
    resultado["partidas"]["formacion_tecnicos"] = {
        "horas": horas_tecnicos,
        "coste": round(coste_tecnicos, 2),
        "texto": texto_tecnicos
    }

    # ————————————————————————————————————————————
    # 3) POLÍTICAS / PROCEDIMIENTOS ENS
    #    Para cada control “política” no cumplido, asignamos HORAS["politica_por_control"] h a €PRECIOS["politica_hora"]/h
    #    Definimos qué códigos ENS interpretamos como “políticas”:
    politicas_controles = [
        "ORG.1", "ORG.2", "ORG.3", "ORG.4",
        "OP.PL.1", "OP.PL.2", "OP.PL.3", "OP.PL.4", "OP.PL.5",
        "OP.ACC.1", "OP.ACC.2", "OP.ACC.4", "OP.ACC.5", "OP.ACC.6",
        "OP.CONT.1",
        "OP.EXP.3"
    ]
    horas_politicas_totales = 0
    coste_politicas_totales = 0
    detalle_politicas = []
    for ctrl in politicas_controles:
        cumple = estado_controles.get(ctrl, False)
        if not cumple:
            h = HORAS["politica_por_control"]
            c = h * PRECIOS["politica_hora"]
            horas_politicas_totales += h
            coste_politicas_totales += c
            detalle_politicas.append(f"{ctrl}: {h} h a €{PRECIOS['politica_hora']}/h ⇒ €{c:.2f}")
    if horas_politicas_totales == 0:
        texto_politicas = "Todas las políticas críticas ya se cumplen (no se requiere redacción adicional)."
    else:
        texto_politicas = (
            "Controles ENS “política” sin cumplir:\n  - " +
            "\n  - ".join(detalle_politicas)
        )
    resultado["partidas"]["politicas"] = {
        "horas": horas_politicas_totales,
        "coste": round(coste_politicas_totales, 2),
        "texto": texto_politicas
    }

    # ————————————————————————————————————————————
    # 4) AUDITORÍA EXTERNA POR SEDE
    # ————————————————————————————————————————————
    horas_auditoria = sedes * HORAS["auditoria_por_sede"]
    coste_auditoria = horas_auditoria * PRECIOS["auditoria_hora"]
    texto_aud = (
        f"{sedes} sedes × {HORAS['auditoria_por_sede']} h = {horas_auditoria} h; "
        f"a €{PRECIOS['auditoria_hora']}/h ⇒ €{coste_auditoria:.2f}."
    )
    resultado["partidas"]["auditoria_externa"] = {
        "horas": horas_auditoria,
        "coste": round(coste_auditoria, 2),
        "texto": texto_aud
    }

    # ————————————————————————————————————————————
    # 5) HERRAMIENTAS TÉCNICAS
    #    a) Antivirus corporativo (Mp.si.1, por ejemplo)
    # ————————————————————————————————————————————
    # Suponemos que “Mp.si.1” equivale a “Antivirus corporativo instalado”.
    tiene_antivirus = estado_controles.get("Mp.si.1", False)
    if not tiene_antivirus:
        coste_antivirus = empleados * PRECIOS["antivirus_por_usuario"]
        texto_av = (
            f"{empleados} usuarios × €{PRECIOS['antivirus_por_usuario']} = €{coste_antivirus:.2f} "
            "(antivirus corporativo)."
        )
    else:
        coste_antivirus = 0
        texto_av = "Antivirus corporativo ya presente."
    resultado["partidas"]["antivirus"] = {
        "horas": 0,
        "coste": round(coste_antivirus, 2),
        "texto": texto_av
    }

    # ————————————————————————————————————————————
    #    b) Firewall perimetral (Mp.si.2)
    # ————————————————————————————————————————————
    tiene_firewall = estado_controles.get("Mp.si.2", False)
    if not tiene_firewall:
        coste_fw = PRECIOS["firewall_perimetral"]
        texto_fw = f"Instalación de firewall perimetral: €{coste_fw:.2f}."
    else:
        coste_fw = 0
        texto_fw = "Firewall perimetral ya implementado."
    resultado["partidas"]["firewall"] = {
        "horas": 0,
        "coste": round(coste_fw, 2),
        "texto": texto_fw
    }

    # ————————————————————————————————————————————
    #    c) SIEM / Plataforma de Logs (Op.mon.1, por ejemplo)
    # ————————————————————————————————————————————
    tiene_siem = estado_controles.get("Op.mon.1", False)  # “Monitorización básica / SIEM”
    if not tiene_siem:
        coste_siem = PRECIOS["siem_inicial"]
        texto_si = f"Instalación/licencia inicial de SIEM o plataforma de logs: €{coste_siem:.2f}."
    else:
        coste_siem = 0
        texto_si = "Plataforma de monitorización/logs ya existente."
    resultado["partidas"]["siem"] = {
        "horas": 0,
        "coste": round(coste_siem, 2),
        "texto": texto_si
    }

    # ————————————————————————————————————————————
    # 6) PLAN DE CONTINUIDAD (Op.cont.1 ya chequeado en ‘políticas’), 
    #    pero si quisiéramos un control extra “continuidad” específico:
    # ————————————————————————————————————————————
    # Ya consideramos “Op.cont.1” dentro de politicas_controles; aquí lo dejamos a cero:
    resultado["partidas"]["plan_continuidad"] = {
        "horas": 0,
        "coste": 0.0,
        "texto": "El control 'Op.cont.1 – Copias de seguridad / continuidad' ya se incluyó en 'políticas'."
    }

    # ————————————————————————————————————————————
    # 7) GESTIÓN DE INCIDENTES (Op.exp.3 ya chequeado en ‘políticas’),
    #    lo dejamos a cero porque ya entró como “política”:
    # ————————————————————————————————————————————
    resultado["partidas"]["gestion_incidentes"] = {
        "horas": 0,
        "coste": 0.0,
        "texto": "El control 'Op.exp.3 – Gestión de Incidentes' ya se incluyó en 'políticas'."
    }

    # ————————————————————————————————————————————
    # 8) OFICIAL DE SEGURIDAD
    # ————————————————————————————————————————————
    tiene_oficial = estado_controles.get("Org.4", False)  # “Org.4 – Proceso de autorización / oficial de seguridad”
    if not tiene_oficial:
        coste_of = PRECIOS["oficial_seguridad_anual"]
        texto_of = f"Contratación de Oficial de Seguridad (20 % dedicación): €{coste_of:.2f}/año."
    else:
        coste_of = 0
        texto_of = "Oficial de Seguridad interno ya designado."
    resultado["partidas"]["oficial_seguridad"] = {
        "horas": 0,
        "coste": round(coste_of, 2),
        "texto": texto_of
    }

    # ————————————————————————————————————————————
    # 9) Totales: sumamos horas y costes iniciales
    # ————————————————————————————————————————————
    total_horas = (
        horas_usuarios +
        horas_tecnicos +
        horas_politicas_totales +
        horas_auditoria
    )
    total_coste_inicial = (
        coste_usuarios +
        coste_tecnicos +
        coste_politicas_totales +
        coste_auditoria +
        coste_antivirus +
        coste_fw +
        coste_siem +
        coste_of
    )

    # ————————————————————————————————————————————
    # 10) Coste recurrente anual (antivirus, auditoría, oficial seguridad, SIEM mantenimiento)
    # ————————————————————————————————————————————
    #   - Renovación antivirus: empleados × €/usuario
    #   - Auditoría anual: coste_auditoria (suponemos 1 vez / año)
    #   - Oficial de seguridad: ya es coste anual completo
    #   - Mantenimiento SIEM: 20 % del coste inicial de SIEM (si se instaló)
    recurrente_anual = 0.0
    recurrente_anual += empleados * PRECIOS["antivirus_por_usuario"]
    recurrente_anual += coste_auditoria
    recurrente_anual += coste_of
    recurrente_anual += 0.2 * coste_siem

    resultado["totales"]["horas_totales"] = total_horas
    resultado["totales"]["coste_total_inicial"] = round(total_coste_inicial, 2)
    resultado["totales"]["coste_recurrente_anual"] = round(recurrente_anual, 2)

    # ————————————————————————————————————————————
    # 11) Plan de Prioridades según PRESUPUESTO
    #    Ordenamos las partidas “con coste > 0” por coste ascendente
    #    e íbamos acumulando hasta llenar el presupuesto.
    # ————————————————————————————————————————————
    partidas_lista = []
    for clave, info in resultado["partidas"].items():
        if info["coste"] > 0:
            partidas_lista.append({
                "clave": clave,
                "coste": info["coste"],
                "texto": info["texto"]
            })

    # Ordenamos por coste ascendente
    partidas_ordenadas = sorted(partidas_lista, key=lambda x: x["coste"])
    acumulado = 0.0
    plan = []
    for item in partidas_ordenadas:
        if acumulado + item["coste"] <= presupuesto:
            acumulado += item["coste"]
            plan.append({
                "concepto": item["clave"],
                "coste": round(item["coste"], 2),
                "acumulado": round(acumulado, 2),
                "texto": item["texto"]
            })
        else:
            break

    resultado["plan_prioridades"] = plan

    return resultado
