"""Tests for the Jinja2 prompt template engine."""

from __future__ import annotations

from typing import TYPE_CHECKING

import pytest

if TYPE_CHECKING:
    from pathlib import Path

from koan.prompts.engine import PromptEngine, PromptRenderError

# -- Tests: render_string (inline templates) --------------------------------

class TestRenderString:
    def test_simple_variable(self) -> None:
        engine = PromptEngine()
        result = engine.render_string("Hello {{ name }}", name="World")
        assert result == "Hello World"

    def test_multiple_variables(self) -> None:
        engine = PromptEngine()
        result = engine.render_string("{{ a }} + {{ b }} = {{ c }}", a="1", b="2", c="3")
        assert result == "1 + 2 = 3"

    def test_conditional(self) -> None:
        engine = PromptEngine()
        tmpl = "{% if verbose %}Detailed mode{% else %}Brief mode{% endif %}"
        assert engine.render_string(tmpl, verbose=True) == "Detailed mode"
        assert engine.render_string(tmpl, verbose=False) == "Brief mode"

    def test_loop(self) -> None:
        engine = PromptEngine()
        tmpl = "{% for item in items %}{{ item }}, {% endfor %}"
        result = engine.render_string(tmpl, items=["a", "b", "c"])
        assert result == "a, b, c, "

    def test_missing_variable_raises(self) -> None:
        engine = PromptEngine()
        with pytest.raises(PromptRenderError, match="Missing variable"):
            engine.render_string("Hello {{ name }}")

    def test_filter_tojson(self) -> None:
        engine = PromptEngine()
        result = engine.render_string("Data: {{ data | tojson }}", data={"key": "value"})
        assert '"key"' in result
        assert '"value"' in result


# -- Tests: render (file-based templates) -----------------------------------

class TestRenderFile:
    def test_builtin_system_template(self) -> None:
        engine = PromptEngine()
        result = engine.render("system.j2", user_name="Alice", role="developer")
        assert "Alice" in result
        assert "developer" in result

    def test_builtin_classify_template(self) -> None:
        engine = PromptEngine()
        result = engine.render(
            "classify.j2",
            categories=["bug", "feature", "question"],
            input="My app crashes on login",
        )
        assert "bug" in result
        assert "feature" in result
        assert "crashes on login" in result

    def test_builtin_tool_use_template(self) -> None:
        engine = PromptEngine()
        tools = [
            {"name": "search", "description": "Search the web", "parameters": {"query": "string"}},
        ]
        result = engine.render("tool_use.j2", tools=tools)
        assert "search" in result
        assert "Search the web" in result

    def test_template_not_found_raises(self) -> None:
        engine = PromptEngine()
        with pytest.raises(PromptRenderError, match="Template not found"):
            engine.render("nonexistent.j2")

    def test_missing_variable_in_file_template_raises(self) -> None:
        engine = PromptEngine()
        with pytest.raises(PromptRenderError, match="Missing variable"):
            engine.render("system.j2")  # Missing user_name and role


# -- Tests: Custom template directories ------------------------------------

class TestCustomTemplateDir:
    def test_custom_dir_takes_precedence(self, tmp_path: Path) -> None:
        # Write a custom template that overrides the builtin
        custom = tmp_path / "system.j2"
        custom.write_text("CUSTOM: {{ user_name }}")

        engine = PromptEngine(template_dirs=[tmp_path])
        result = engine.render("system.j2", user_name="Bob", role="admin")
        assert result == "CUSTOM: Bob"

    def test_fallback_to_builtin(self, tmp_path: Path) -> None:
        # Custom dir exists but doesn't have classify.j2 → falls back to builtin
        engine = PromptEngine(template_dirs=[tmp_path])
        result = engine.render(
            "classify.j2",
            categories=["a", "b"],
            input="test",
        )
        assert "a, b" in result
