"""Motor de templates minimalista sin dependencias externas."""

import re


_HTML_ESCAPES = {
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    '"': "&quot;",
    "'": "&#x27;",
}


def _html_escape(value: str) -> str:
    for ch, entity in _HTML_ESCAPES.items():
        value = value.replace(ch, entity)
    return value


def _resolve_path(context: dict, path: str, missing: str) -> str:
    """Resuelve un dot-path en un dict anidado."""
    parts = path.strip().split(".")
    current = context
    for part in parts:
        if isinstance(current, dict) and part in current:
            current = current[part]
        else:
            return missing
    return str(current) if current is not None else ""


def _process_block(template: str, context: dict, missing: str) -> str:
    """Procesa un bloque de template (puede contener for/if anidados)."""
    result = template

    # Procesar bloques for: {% for x in items %}...{% endfor %}
    for_pattern = re.compile(
        r"\{%\s*for\s+(\w+)\s+in\s+(\w+(?:\.\w+)*)\s*%\}(.*?)\{%\s*endfor\s*%\}",
        re.DOTALL,
    )
    while for_pattern.search(result):
        def replace_for(m: re.Match) -> str:
            var_name = m.group(1)
            list_path = m.group(2)
            body = m.group(3)
            items = _resolve_raw(context, list_path)
            if not isinstance(items, list):
                return ""
            parts = []
            for item in items:
                loop_context = dict(context)
                loop_context[var_name] = item
                parts.append(_process_block(body, loop_context, missing))
            return "".join(parts)

        result = for_pattern.sub(replace_for, result)

    # Procesar bloques if not: {% if not cond %}...{% endif %}
    if_not_pattern = re.compile(
        r"\{%\s*if\s+not\s+(\w+(?:\.\w+)*)\s*%\}(.*?)\{%\s*endif\s*%\}",
        re.DOTALL,
    )
    while if_not_pattern.search(result):
        def replace_if_not(m: re.Match) -> str:
            cond_path = m.group(1)
            body = m.group(2)
            value = _resolve_raw(context, cond_path)
            if not value:
                return _process_block(body, context, missing)
            return ""

        result = if_not_pattern.sub(replace_if_not, result)

    # Procesar bloques if: {% if cond %}...{% endif %}
    if_pattern = re.compile(
        r"\{%\s*if\s+(\w+(?:\.\w+)*)\s*%\}(.*?)\{%\s*endif\s*%\}",
        re.DOTALL,
    )
    while if_pattern.search(result):
        def replace_if(m: re.Match) -> str:
            cond_path = m.group(1)
            body = m.group(2)
            value = _resolve_raw(context, cond_path)
            if value:
                return _process_block(body, context, missing)
            return ""

        result = if_pattern.sub(replace_if, result)

    # Sustitucion triple braces (sin escape): {{{var}}}
    raw_pattern = re.compile(r"\{\{\{(\s*[\w.]+\s*)\}\}\}")
    result = raw_pattern.sub(
        lambda m: _resolve_path(context, m.group(1), missing), result
    )

    # Sustitucion doble braces (HTML-escaped): {{var}}
    escaped_pattern = re.compile(r"\{\{(\s*[\w.]+\s*)\}\}")
    result = escaped_pattern.sub(
        lambda m: _html_escape(_resolve_path(context, m.group(1), missing)), result
    )

    return result


def _resolve_raw(context: dict, path: str):
    """Resuelve un dot-path y retorna el valor raw (no convertido a string)."""
    parts = path.strip().split(".")
    current = context
    for part in parts:
        if isinstance(current, dict) and part in current:
            current = current[part]
        else:
            return None
    return current


def render_template(template: str, context: dict, missing: str = "") -> str:
    """Motor de templates minimalista sin dependencias.

    Soporta sustitucion de variables, dot-path traversal, bucles for
    e condicionales if/if not. No pretende reemplazar Jinja2 — cubre
    el 80% de casos simples.

    Sintaxis:
        {{var}}             — sustitucion con HTML escape
        {{{var}}}           — sustitucion sin escape (raw)
        {{obj.key.subkey}}  — dot-path traversal en dicts anidados
        {% for x in items %}...{% endfor %} — iteracion sobre lista
        {% if cond %}...{% endif %}          — condicional truthy
        {% if not cond %}...{% endif %}      — negacion

    Args:
        template: String con la plantilla.
        context: Dict con los valores disponibles para sustitucion.
        missing: Valor por defecto para variables no encontradas.
                 Por defecto string vacio.

    Returns:
        String con todos los placeholders procesados.
    """
    return _process_block(template, context, missing)
