"""Tests for TemplateRenderer — Word template filling with Jinja2 sandbox (U5)."""

from __future__ import annotations

from pathlib import Path

import pytest
from docx import Document

from agentkit.documents.renderers.template_renderer import TemplateRenderer


def _make_template(tmp_path: Path, content: str) -> Path:
    """Create a .docx template with the given text content (single paragraph)."""
    template_path = tmp_path / "template.docx"
    doc = Document()
    doc.add_paragraph(content)
    doc.save(str(template_path))
    return template_path


def _read_text(path: Path) -> str:
    """Read all paragraph text from a .docx file."""
    doc = Document(str(path))
    return "\n".join(p.text for p in doc.paragraphs)


def test_simple_variable_substitution(tmp_path: Path) -> None:
    """{{name}} is replaced with data['name']."""
    template = _make_template(tmp_path, "Hello, {{name}}!")
    output = tmp_path / "output.docx"
    TemplateRenderer().render_template(template, {"name": "张三"}, output)
    assert _read_text(output) == "Hello, 张三!"


def test_multiple_variables(tmp_path: Path) -> None:
    """Multiple {{var}} placeholders are all filled."""
    template = _make_template(tmp_path, "{{greeting}}, {{name}}. You are {{role}}.")
    output = tmp_path / "output.docx"
    TemplateRenderer().render_template(
        template, {"greeting": "Hi", "name": "Alice", "role": "admin"}, output
    )
    assert _read_text(output) == "Hi, Alice. You are admin."


def test_for_loop(tmp_path: Path) -> None:
    """{% for %} loop expands correctly."""
    # Create a template with a for loop in a single paragraph
    template_path = tmp_path / "template.docx"
    doc = Document()
    # docxtpl requires the for loop tags in the paragraph
    doc.add_paragraph("{% for item in items %}{{item}} {% endfor %}")
    doc.save(str(template_path))

    output = tmp_path / "output.docx"
    TemplateRenderer().render_template(template_path, {"items": ["A", "B", "C"]}, output)
    text = _read_text(output)
    assert "A" in text
    assert "B" in text
    assert "C" in text


def test_if_condition(tmp_path: Path) -> None:
    """{% if %} conditional renders content when condition is true."""
    template_path = tmp_path / "template.docx"
    doc = Document()
    doc.add_paragraph("{% if show %}Visible{% endif %}")
    doc.save(str(template_path))

    output = tmp_path / "output.docx"
    TemplateRenderer().render_template(template_path, {"show": True}, output)
    assert "Visible" in _read_text(output)


def test_if_condition_false(tmp_path: Path) -> None:
    """{% if %} conditional hides content when condition is false."""
    template_path = tmp_path / "template.docx"
    doc = Document()
    doc.add_paragraph("{% if show %}Visible{% endif %}")
    doc.save(str(template_path))

    output = tmp_path / "output.docx"
    TemplateRenderer().render_template(template_path, {"show": False}, output)
    assert "Visible" not in _read_text(output)


def test_template_not_found(tmp_path: Path) -> None:
    """Missing template file raises FileNotFoundError."""
    output = tmp_path / "output.docx"
    with pytest.raises(FileNotFoundError, match="Template not found"):
        TemplateRenderer().render_template(
            tmp_path / "nonexistent.docx", {}, output
        )


def test_no_placeholders(tmp_path: Path) -> None:
    """Template with no Jinja2 tags is output unchanged."""
    template = _make_template(tmp_path, "Just plain text, no variables.")
    output = tmp_path / "output.docx"
    TemplateRenderer().render_template(template, {}, output)
    assert _read_text(output) == "Just plain text, no variables."


def test_ssti_blocked(tmp_path: Path) -> None:
    """Sandbox blocks access to dunder attributes (SSTI protection).

    {{config.__class__}} should not expose Python internals. Jinja2's
    SandboxedEnvironment returns Undefined for attributes starting with
    '_', so the output is empty rather than raising — the key security
    property is that internal class info is never leaked.
    """
    template = _make_template(tmp_path, "{{config.__class__}}")
    output = tmp_path / "output.docx"
    # Should not raise (SandboxedEnvironment returns Undefined), but
    # critically should NOT expose class info.
    TemplateRenderer().render_template(template, {"config": {}}, output)
    text = _read_text(output)
    # The dunder access is blocked — no class info leaks
    assert "dict" not in text.lower()
    assert "class" not in text.lower()
    assert "{{" not in text  # placeholder is consumed (replaced with empty)


def test_ssti_globals_blocked(tmp_path: Path) -> None:
    """Sandbox blocks __globals__ access (deeper SSTI payload)."""
    template = _make_template(
        tmp_path, "{{config.__class__.__init__.__globals__}}"
    )
    output = tmp_path / "output.docx"
    TemplateRenderer().render_template(template, {"config": {}}, output)
    text = _read_text(output)
    # No globals should leak
    assert "builtins" not in text.lower()
    assert "import" not in text.lower()


def test_missing_variable(tmp_path: Path) -> None:
    """Missing variable in data dict — Jinja2 default behavior (empty string)."""
    template = _make_template(tmp_path, "Hello, {{name}}!")
    output = tmp_path / "output.docx"
    # With no 'name' in data, Jinja2 SandboxedEnvironment defaults to undefined
    # which renders as empty string (not an error)
    TemplateRenderer().render_template(template, {}, output)
    text = _read_text(output)
    # The placeholder should be gone (replaced with empty)
    assert "{{name}}" not in text
