"""Unit tests for template models."""

import pytest
from markitect.prompts.templates.models import (
    ContentMacro,
    MacroKind,
    PromptTemplate,
    TemplateMetadata,
)
from markitect.prompts.models import ArtifactType


class TestContentMacro:
    """Tests for ContentMacro."""

    def test_create_required_macro(self):
        """Test creating required macro."""
        macro = ContentMacro(
            kind=MacroKind.REQUIRED,
            target="glossary",
        )
        assert macro.kind == MacroKind.REQUIRED
        assert macro.target == "glossary"
        assert macro.parameters == {}

    def test_create_macro_with_parameters(self):
        """Test creating macro with parameters."""
        macro = ContentMacro(
            kind=MacroKind.GENERATE,
            target="code-examples",
            parameters={"language": "python", "framework": "fastapi"},
        )
        assert macro.parameters == {"language": "python", "framework": "fastapi"}

    def test_programmatic_macro_gets_auto_derived_raw_text(self):
        """Test that programmatically-built macro gets auto-derived raw_text."""
        macro = ContentMacro(
            kind=MacroKind.REQUIRED,
            target="glossary",
        )
        assert macro.raw_text == "@{glossary}"

    def test_explicit_raw_text_not_overridden(self):
        """Test that explicit raw_text is preserved."""
        macro = ContentMacro(
            kind=MacroKind.REQUIRED,
            target="glossary",
            raw_text="{{require:glossary}}",
        )
        assert macro.raw_text == "{{require:glossary}}"

    def test_macro_str_representation(self):
        """Test string representation."""
        macro = ContentMacro(
            kind=MacroKind.REQUIRED,
            target="test",
        )
        assert str(macro) == "{{required:test}}"

    def test_macro_str_with_parameters(self):
        """Test string representation with parameters."""
        macro = ContentMacro(
            kind=MacroKind.GENERATE,
            target="gen",
            parameters={"key": "value"},
        )
        assert "generate:gen" in str(macro)
        assert "key=value" in str(macro)

    def test_macro_to_dict(self):
        """Test serialization to dict."""
        macro = ContentMacro(
            kind=MacroKind.OPTIONAL,
            target="test",
            parameters={"a": "1"},
            raw_text="{{optional:test|a=1}}",
            line_number=42,
        )
        data = macro.to_dict()
        assert data["kind"] == "optional"
        assert data["target"] == "test"
        assert data["parameters"] == {"a": "1"}
        assert data["line_number"] == 42

    def test_macro_from_dict(self):
        """Test deserialization from dict."""
        data = {
            "kind": "required",
            "target": "test",
            "parameters": {"x": "y"},
            "raw_text": "{{require:test|x=y}}",
            "line_number": 10,
        }
        macro = ContentMacro.from_dict(data)
        assert macro.kind == MacroKind.REQUIRED
        assert macro.target == "test"
        assert macro.parameters == {"x": "y"}
        assert macro.line_number == 10


class TestTemplateMetadata:
    """Tests for TemplateMetadata."""

    def test_create_empty_metadata(self):
        """Test creating empty metadata."""
        meta = TemplateMetadata()
        assert meta.purpose is None
        assert meta.model_hints == {}
        assert meta.expected_inputs == []
        assert meta.output_type is None

    def test_create_metadata_with_values(self):
        """Test creating metadata with values."""
        meta = TemplateMetadata(
            purpose="Generate API docs",
            model_hints={"temperature": 0.7},
            expected_inputs=["api-spec", "examples"],
            output_type="markdown",
        )
        assert meta.purpose == "Generate API docs"
        assert meta.model_hints == {"temperature": 0.7}
        assert meta.expected_inputs == ["api-spec", "examples"]
        assert meta.output_type == "markdown"

    def test_metadata_to_dict(self):
        """Test serialization."""
        meta = TemplateMetadata(purpose="Test")
        data = meta.to_dict()
        assert data["purpose"] == "Test"
        assert "model_hints" in data

    def test_metadata_from_dict(self):
        """Test deserialization."""
        data = {
            "purpose": "Test",
            "model_hints": {"temp": 0.5},
            "expected_inputs": ["in1"],
            "output_type": "json",
        }
        meta = TemplateMetadata.from_dict(data)
        assert meta.purpose == "Test"
        assert meta.model_hints == {"temp": 0.5}


class TestPromptTemplate:
    """Tests for PromptTemplate."""

    def test_create_template(self):
        """Test template creation."""
        template = PromptTemplate.create(
            space_id="space-1",
            name="test-template",
            content="# Template\n{{require:glossary}}",
        )
        assert template.space_id == "space-1"
        assert template.name == "test-template"
        assert template.artifact.artifact_type == ArtifactType.TEMPLATE
        assert not template.analyzed
        assert template.macros == []

    def test_template_properties(self):
        """Test template properties."""
        template = PromptTemplate.create(
            space_id="space-1",
            name="test",
            content="content",
        )
        assert template.id  # Has UUID
        assert template.space_id == "space-1"
        assert template.name == "test"
        assert template.content_digest  # Has digest

    def test_get_required_dependencies(self):
        """Test extracting required dependencies."""
        template = PromptTemplate.create(
            space_id="space-1",
            name="test",
            content="content",
        )
        template.macros = [
            ContentMacro(kind=MacroKind.REQUIRED, target="dep1"),
            ContentMacro(kind=MacroKind.OPTIONAL, target="dep2"),
            ContentMacro(kind=MacroKind.REQUIRED, target="dep3"),
        ]
        required = template.get_required_dependencies()
        assert required == ["dep1", "dep3"]

    def test_get_optional_dependencies(self):
        """Test extracting optional dependencies."""
        template = PromptTemplate.create(
            space_id="space-1",
            name="test",
            content="content",
        )
        template.macros = [
            ContentMacro(kind=MacroKind.REQUIRED, target="dep1"),
            ContentMacro(kind=MacroKind.OPTIONAL, target="dep2"),
            ContentMacro(kind=MacroKind.OPTIONAL, target="dep3"),
        ]
        optional = template.get_optional_dependencies()
        assert optional == ["dep2", "dep3"]

    def test_get_generators(self):
        """Test extracting generators."""
        template = PromptTemplate.create(
            space_id="space-1",
            name="test",
            content="content",
        )
        template.macros = [
            ContentMacro(kind=MacroKind.GENERATE, target="gen1"),
            ContentMacro(kind=MacroKind.REQUIRED, target="dep1"),
            ContentMacro(kind=MacroKind.GENERATE, target="gen2"),
        ]
        generators = template.get_generators()
        assert generators == ["gen1", "gen2"]

    def test_from_artifact_invalid_type(self):
        """Test from_artifact raises error for non-template."""
        from markitect.prompts.models import Artifact
        artifact = Artifact.create(
            space_id="space-1",
            name="not-template",
            content="content",
            artifact_type=ArtifactType.CONTENT,
        )
        with pytest.raises(ValueError, match="must be of type TEMPLATE"):
            PromptTemplate.from_artifact(artifact)

    def test_template_to_dict(self):
        """Test serialization."""
        template = PromptTemplate.create(
            space_id="space-1",
            name="test",
            content="content",
        )
        template.analyzed = True
        data = template.to_dict()
        assert "artifact" in data
        assert "template_metadata" in data
        assert data["analyzed"] is True

    def test_template_from_dict(self):
        """Test deserialization."""
        original = PromptTemplate.create(
            space_id="space-1",
            name="test",
            content="content",
        )
        data = original.to_dict()
        restored = PromptTemplate.from_dict(data)
        assert restored.id == original.id
        assert restored.name == original.name
