import json

# 1) Diccionario con título y descripción breve de cada tipo de alerta
CONTROLES_DESCRIPCION_INC = {
    "Windows_FuerzaBruta": {
        "titulo": "Windows – Fuerza Bruta",
        "descripcion": (
            "Se han detectado múltiples intentos fallidos de inicio de sesión en un corto periodo. "
            "ENS Nivel Alto (OP.MON.2) exige bloquear cuentas tras X intentos y generar alerta."
        )
    },
    "Windows_FuerzaBrutaConExito": {
        "titulo": "Windows – Fuerza Bruta con Éxito",
        "descripcion": (
            "Se registró un inicio de sesión exitoso tras varios fallos. ENS Alto (OP.MON.2) requiere "
            "investigación inmediata para descartar acceso no autorizado."
        )
    },
    "Windows_CreacionCuenta": {
        "titulo": "Windows – Creación de Cuenta",
        "descripcion": (
            "Se ha creado una cuenta local en Windows. ENS Nivel Alto (OP.MON.1 / OP.MON.3) exige trazabilidad "
            "de creación y justificación de cuentas."
        )
    },
    "Windows_HabilitacionCuenta": {
        "titulo": "Windows – Habilitación de Cuenta",
        "descripcion": (
            "Se detectó que una cuenta local fue habilitada. ENS Alto (OP.MON.3) requiere notificar "
            "cambios en estado de cuentas con elevación de privilegios."
        )
    },
    "Windows_ElevacionGlobal": {
        "titulo": "Windows – Elevación a Grupo Global",
        "descripcion": (
            "Un usuario fue agregado a un grupo global (posiblemente Administradores). ENS Alto (OP.MON.3) exige revisión de elevaciones no autorizadas."
        )
    },
    "Windows_ElevacionLocal": {
        "titulo": "Windows – Elevación a Grupo Local",
        "descripcion": (
            "Se agregó un usuario a un grupo local privilegiado. ENS Alto (OP.MON.3) requiere control y auditoría de elevaciones de privilegios."
        )
    },
    "Windows_Firewall_DROP": {
        "titulo": "Windows – Firewall DROP",
        "descripcion": (
            "El firewall bloqueó tráfico en un puerto específico. ENS Alto (OP.MON.2) aconseja revisar si el tráfico bloqueado era malicioso o legítimo."
        )
    },
    "Windows_Firewall_ALLOW": {
        "titulo": "Windows – Firewall ALLOW",
        "descripcion": (
            "El firewall permitió tráfico en un puerto específico. ENS Alto recomienda auditar puertos abiertos y cerrarlos si no son necesarios."
        )
    },
    "Linux_InvalidUser": {
        "titulo": "Linux – Intento con Usuario Inválido",
        "descripcion": (
            "Un intento SSH con un usuario que no existe. ENS Alto (OP.MON.2) exige monitorizar fallos de autenticación para prevenir ataques de diccionario."
        )
    },
    "Linux_FuerzaBrutaSSH": {
        "titulo": "Linux – Fuerza Bruta SSH",
        "descripcion": (
            "Múltiples intentos fallidos de autenticación SSH en poco tiempo. ENS Alto (OP.MON.2) requiere alertar y bloquear dichos intentos (p. ej. fail2ban)."
        )
    },
    "Linux_FuerzaBrutaConExitoSSH": {
        "titulo": "Linux – SSH Exitoso tras Fallos",
        "descripcion": (
            "Se detectó un inicio de sesión SSH exitoso tras varios fallos. ENS Alto (OP.MON.2) exige investigar si se trata de un atacante que finalmente obtuvo credenciales."
        )
    },
    "Linux_FileModificado": {
        "titulo": "Linux – Archivo Crítico Modificado",
        "descripcion": (
            "Un archivo esencial (/etc/passwd, /etc/shadow, /etc/ssh/sshd_config) ha cambiado. ENS Nivel Alto (OP.MON.3) exige control de integridad y auditoría de cambios en archivos críticos."
        )
    }
}


def generar_recomendacion_incidente(obj: dict) -> str:
    """
    Según obj['tipo'], devuelve un texto recomendando acciones para ENS Nivel Alto.
    """
    tipo = obj.get("tipo", "")
    if tipo == "Windows_FuerzaBruta":
        usuario = obj.get("usuario", "desconocido")
        ip = obj.get("origen", "desconocido")
        return (
            f"Se detectaron {obj.get('num_fallos', '?')} fallos de inicio de sesión para '{usuario}' desde {ip}. "
            "ENS Alto (OP.MON.2) exige bloquear cuentas tras 3–5 intentos y notificar. "
            "Recomendación: configure 'Account Lockout Policy' para 3–5 fallos, bloquee IP sospechosa y revise logs de seguridad."
        )

    if tipo == "Windows_FuerzaBrutaConExito":
        usuario = obj.get("usuario", "desconocido")
        ip = obj.get("origen", "desconocido")
        return (
            f"Inicio de sesión exitoso tras múltiples fallos para '{usuario}' desde {ip}. "
            "ENS Alto (OP.MON.2) requiere auditoría forense inmediata. "
            "Recomendación: cambiar contraseña, bloquear IP sospechosa y revisar si el acceso fue legítimo."
        )

    if tipo == "Windows_CreacionCuenta":
        new_user = obj.get("usuario", "desconocido")
        return (
            f"Se ha creado la cuenta local '{new_user}' sin registro previo. "
            "ENS Alto (OP.MON.1 / OP.MON.3) exige trazabilidad de creación de usuarios. "
            "Recomendación: confirmar si '{new_user}' está documentada; si no, deshabilitarla o eliminarla."
        )

    if tipo == "Windows_HabilitacionCuenta":
        user = obj.get("usuario", "desconocido")
        return (
            f"La cuenta '{user}' ha sido habilitada. ENS Alto (OP.MON.3) exige notificar cambios de estado de cuentas. "
            "Recomendación: verifique si '{user}' debe ser privilegiada; si no, revoque permisos y configure alertas de habilitación."
        )

    if tipo == "Windows_ElevacionGlobal" or tipo == "Windows_ElevacionLocal":
        grupo = obj.get("grupo", "desconocido")
        return (
            f"Se agregó a '{grupo}' un nuevo miembro con privilegios. ENS Alto (OP.MON.3) requiere auditoría de "
            "elevación de privilegios. Recomendación: confirme si el usuario agregado es legítimo; de lo contrario, "
            "revoque permisos y realice un análisis de acceso."
        )

    if tipo == "Windows_Firewall_DROP":
        ip_src = obj.get("ip_origen", "desconocido")
        puerto = obj.get("puerto_destino", "desconocido")
        return (
            f"Firewall bloqueó tráfico desde {ip_src} al puerto {puerto}. ENS Alto (OP.MON.2) aconseja revisar si "
            "este tráfico era malicioso o necesario. Recomendación: compruebe reglas de firewall y realice análisis de tráfico."
        )

    if tipo == "Windows_Firewall_ALLOW":
        ip_src = obj.get("ip_origen", "desconocido")
        puerto = obj.get("puerto_destino", "desconocido")
        return (
            f"Firewall permitió tráfico desde {ip_src} al puerto {puerto}. ENS Alto recomienda auditar puertos abiertos "
            "y cerrar aquellos que no sean esenciales. Recomendación: verificar servicios escuchando."
        )

    if tipo == "Linux_InvalidUser":
        user = obj.get("usuario", "desconocido")
        ip_src = obj.get("origen", "desconocido")
        return (
            f"Intento SSH con usuario inválido '{user}' desde {ip_src}. ENS Alto (OP.MON.2) exige detectar ataques de diccionario. "
            "Recomendación: bloquear IP, deshabilitar login por contraseña en SSH y usar fail2ban."
        )

    if tipo == "Linux_FuerzaBrutaSSH":
        user = obj.get("usuario", "desconocido")
        ip_src = obj.get("origen", "desconocido")
        return (
            f"{obj.get('num_fallos', '?')} fallos de autenticación SSH para '{user}' desde {ip_src} en {obj.get('ventana_min', '?')} minutos. "
            "ENS Alto (OP.MON.2) exige bloqueo tras 3–5 intentos. Recomendación: instalar/configurar fail2ban o pam_faillock y revisar logs."
        )

    if tipo == "Linux_FuerzaBrutaConExitoSSH":
        user = obj.get("usuario", "desconocido")
        ip_src = obj.get("origen", "desconocido")
        return (
            f"Inicio de sesión SSH exitoso tras múltiples fallos para '{user}' desde {ip_src}. ENS Alto (OP.MON.2) exige investigación forense. "
            "Recomendación: cambie contraseña, aísle el servidor y revise logs para identificar cambios."
        )

    if tipo == "Linux_FileModificado":
        ruta = obj.get("ruta", "desconocida")
        return (
            f"Se detectó modificación en archivo crítico '{ruta}'. ENS Alto (OP.MON.3) requiere control de integridad de archivos. "
            "Recomendación: confirmar si el cambio fue autorizado (parche/actualización). Si no, restaurar desde backup y ajustar permisos."
        )

    # Si hay un tipo no contemplado:
    return "Revisar manualmente este incidente. No se encontró recomendación automática."


def parsear_informe_incidentes(filepath: str) -> dict:
    """
    Lee un fichero multilínea que contiene bloques JSON. Agrupa cada bloque entre '{' y su '}', 
    y luego hace json.loads(...) sobre el bloque completo.
    Devuelve un dict: { tipo1: [ {timestamp, mensaje, recomendacion}, … ], tipo2: […], … }.
    """
    informe = { clave: [] for clave in CONTROLES_DESCRIPCION_INC }

    with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
        collecting = False
        bloque = []

        for linea in f:
            linea_stripped = linea.strip()

            # Si vemos el inicio de un bloque JSON
            if linea_stripped.startswith("{"):
                collecting = True
                bloque = [linea_stripped]
                continue

            # Si estamos dentro de un bloque JSON, vamos añadiendo líneas
            if collecting:
                bloque.append(linea_stripped)
                # Si la línea finaliza con '}', cerramos el bloque
                if linea_stripped.endswith("}"):
                    collecting = False
                    # Unimos todas las líneas en un solo string JSON
                    contenido_json = "\n".join(bloque)
                    bloque = []

                    try:
                        obj = json.loads(contenido_json)
                    except Exception:
                        # Si falla el JSON, lo descartamos
                        continue

                    tipo = obj.get("tipo")
                    if tipo not in informe:
                        # Ignoramos tipos que no conocemos
                        continue

                    timestamp = obj.get("timestamp", "")
                    # Guardamos el bloque JSON tal cual (o podríamos reconstruir un mensaje más corto)
                    mensaje = contenido_json

                    rec = generar_recomendacion_incidente(obj)
                    informe[tipo].append({
                        "timestamp": timestamp,
                        "mensaje": mensaje,
                        "recomendacion": rec
                    })

                # En cualquier caso, saltamos a la siguiente línea
                continue

            # Si no estamos recogiendo JSON y la línea no empieza con '{', la ignoramos
            # (por ejemplo, las líneas "[ALERTA] …")
            # Así evitamos intentar json.loads("   ") o json.loads("[ALERTA]…")
            # y solo procesamos cuando encontremos un bloque que inicie con '{'.

    return informe
