"""
Composicion del correo de notificacion de sistema.

Los sistemas internos mandan `system` y `notification` con algo de formato
HTML dentro (`<b>`, `<em>`, saltos de linea), y eso hay que conservarlo. Lo
que no se puede conservar es el `|safe` de la plantilla, que inserta el texto
sin filtrar ninguna etiqueta.

Con `|safe`, quien tenga una credencial de esta API puede meter en el correo
un enlace, un formulario o una imagen remota que salga firmado por el dominio
de la empresa. Es la materia prima de un correo de suplantacion creible.

La solucion no es escapar todo, porque romperia el formato que ya usan, sino
sanear: se permite un conjunto acotado de etiquetas de formato y se elimina
todo lo demas.
"""

from __future__ import annotations

import logging

import nh3
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe

logger = logging.getLogger("notifications.templates")

#: Etiquetas de formato admitidas en el texto que mandan los sistemas.
#: Deliberadamente NO estan `a`, `img`, `script`, `style`, `iframe`, `form`
#: ni `object`: son las que permiten construir un correo de suplantacion o
#: filtrar la apertura a un servidor de terceros.
ETIQUETAS_PERMITIDAS = {
    "b", "strong", "i", "em", "u", "s", "br", "p", "span",
    "ul", "ol", "li", "code", "small",
}

#: Ningun atributo. Sin esto se colarian `style`, `onerror` o `href`.
ATRIBUTOS_PERMITIDOS: dict[str, set[str]] = {}


def sanear(texto: str) -> str:
    """Elimina del texto toda etiqueta que no sea de formato basico."""
    if not texto:
        return ""
    limpio = nh3.clean(
        texto,
        tags=ETIQUETAS_PERMITIDAS,
        attributes=ATRIBUTOS_PERMITIDOS,
        link_rel=None,
    )
    if limpio != texto:
        logger.info(
            "Se elimino contenido HTML no permitido de una notificacion",
            extra={"longitud_original": len(texto), "longitud_final": len(limpio)},
        )
    return limpio


def render_notification_email(system: str, notification: str) -> tuple[str, str]:
    """
    Devuelve `(asunto, html)` del correo de notificacion de sistema.

    El asunto se construye con `system` ya saneado y sin etiquetas, porque una
    cabecera no puede llevar HTML.
    """
    system_limpio = sanear(system)
    notification_limpia = sanear(notification)

    html = render_to_string(
        "notification_email.html",
        {
            # `mark_safe` es correcto aqui y solo aqui: el contenido acaba de
            # pasar por el saneador, asi que ya no puede traer etiquetas
            # peligrosas. La plantilla no lleva `|safe`.
            "system": mark_safe(system_limpio),  # noqa: S308
            "notification": mark_safe(notification_limpia),  # noqa: S308
        },
    )

    asunto = f"Notificación del sistema: {nh3.clean(system, tags=set())}"
    return asunto[:255], html
