"""Generación de XML de Eventos (con envelope SOAP).

Este módulo construye estructuras mínimas para eventos SIFEN y las envuelve
en SOAP 1.2, siguiendo el patrón del `xmlgen` original.
"""

from __future__ import annotations

import datetime as _dt

from lxml import etree

from ..config import XmlgenConfig
from ..utils import normalize_xml, to_json_datetime


_EVENT_TYPE_TO_CODE: dict[str, int] = {
    "cancelacion": 1,
    "inutilizacion": 2,
    "conformidad": 11,
    "disconformidad": 12,
    "desconocimiento": 13,
    "notificacion": 14,
    "nominacion": 15,
    "actualizacion_datos_transporte": 16,
}


def _soap_envelope(event_id: int, xml_inner: str) -> str:
    """Envuelve el XML interno del evento en un envelope SOAP."""
    return (
        "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
        "<env:Envelope xmlns:env=\"http://www.w3.org/2003/05/soap-envelope\">"
        "<env:Header/>"
        "<env:Body>"
        "<rEnviEventoDe xmlns=\"http://ekuatia.set.gov.py/sifen/xsd\">"
        f"<dId>{event_id}</dId>"
        f"<dEvReg>{xml_inner}</dEvReg>"
        "</rEnviEventoDe>"
        "</env:Body>"
        "</env:Envelope>"
    )


def generate_event(event_id: int, params: dict, data: dict, event_type: str, config: XmlgenConfig) -> str:
    """Genera el XML completo del evento (SOAP + payload)."""
    if event_type not in _EVENT_TYPE_TO_CODE:
        raise ValueError(f"event_type no soportado: {event_type}")

    tipo_evento = _EVENT_TYPE_TO_CODE[event_type]

    nsmap = {
        None: "http://ekuatia.set.gov.py/sifen/xsd",
        "xsi": "http://www.w3.org/2001/XMLSchema-instance",
    }
    root = etree.Element("gGroupGesEve", nsmap=nsmap)
    root.set(
        "{http://www.w3.org/2001/XMLSchema-instance}schemaLocation",
        "http://ekuatia.set.gov.py/sifen/xsd siRecepEvento_v150.xsd",
    )

    rGesEve = etree.SubElement(root, "rGesEve")
    rEve = etree.SubElement(rGesEve, "rEve")
    rEve.set("Id", "1")

    fecha_firma = data.get("fechaFirmaDigital")
    if fecha_firma is None:
        dt = _dt.datetime.now()
    elif isinstance(fecha_firma, str):
        dt = _dt.datetime.fromisoformat(fecha_firma)
    else:
        dt = fecha_firma

    etree.SubElement(rEve, "dFecFirma").text = to_json_datetime(dt)
    etree.SubElement(rEve, "dVerFor").text = str(params.get("version", ""))

    gGroupTiEvt = etree.SubElement(rEve, "gGroupTiEvt")
    # Nota: para "cancelacion" el formato esperado por SET no incluye iTipEvt.
    if event_type != "cancelacion":
        etree.SubElement(gGroupTiEvt, "iTipEvt").text = str(tipo_evento)

    # Payload por tipo de evento
    if event_type == "cancelacion":
        cdc = (data.get("cdc") or "").strip()
        motivo = (data.get("motivo") or "").strip()
        if not cdc:
            raise ValueError("Debe proporcionar el CDC en data.cdc")
        if len(cdc) != 44:
            raise ValueError("El CDC en data.cdc debe tener 44 caracteres")
        if not motivo:
            raise ValueError("Debe proporcionar el Motivo de la Cancelación en data.motivo")
        if not (5 <= len(motivo) <= 500):
            raise ValueError("El Motivo de la Cancelación en data.motivo debe contener de [5-500] caracteres")

        rGeVeCan = etree.SubElement(gGroupTiEvt, "rGeVeCan")
        etree.SubElement(rGeVeCan, "Id").text = cdc
        etree.SubElement(rGeVeCan, "mOtEve").text = motivo

    xml_bytes = etree.tostring(
        root,
        xml_declaration=False,
        encoding="UTF-8",
    )
    inner = normalize_xml(xml_bytes.decode("utf-8"))

    return _soap_envelope(event_id=event_id, xml_inner=inner)
