"""Tests for inject/compose functionality."""

from __future__ import annotations

import pytest

from sigil.models import (
    BehavioralEntry,
    Identity,
    Persona,
    PersonaDocument,
    Value,
    Voice,
)


def make_persona(
    soul: str = "",
    voice: Voice | None = None,
    values: list[Value] | None = None,
    behavioral_map: dict[str, BehavioralEntry] | None = None,
) -> Persona:
    """Helper to create a Persona for testing."""
    return Persona(
        name="test",
        version="1.0.0",
        document=PersonaDocument(
            identity=Identity(name="test", version="1.0.0"),
            soul=soul,
            voice=voice or Voice(),
            values=values or [],
            behavioral_map=behavioral_map or {},
            test_cases=[],
        ),
    )


class TestInject:
    """Test the inject() method."""

    def test_inject_soul_only(self) -> None:
        persona = make_persona(soul="You are a helpful assistant.")
        result = persona.inject()
        assert result == "You are a helpful assistant."

    def test_inject_with_base_prompt(self) -> None:
        persona = make_persona(soul="You are a helpful assistant.")
        result = persona.inject(base="Base context here.")
        assert result.startswith("Base context here.")
        assert "You are a helpful assistant." in result

    def test_inject_with_voice_dos_donts(self) -> None:
        voice = Voice(
            dos=["Be clear", "Be concise"],
            donts=["Ramble", "Overcomplicate"],
        )
        persona = make_persona(soul="Core soul.", voice=voice)
        result = persona.inject()

        assert "Core soul." in result
        assert "## Do" in result
        assert "- Be clear" in result
        assert "- Be concise" in result
        assert "## Don't" in result
        assert "- Ramble" in result
        assert "- Overcomplicate" in result

    def test_inject_with_tone(self) -> None:
        voice = Voice(tone=["professional", "friendly"])
        persona = make_persona(voice=voice)
        result = persona.inject()

        assert "## Tone" in result
        assert "- professional" in result
        assert "- friendly" in result

    def test_inject_with_example_phrases(self) -> None:
        voice = Voice(example_phrases=["Hello!", "Let me help."])
        persona = make_persona(voice=voice)
        result = persona.inject()

        assert "## Example Phrases" in result
        assert '"Hello!"' in result
        assert '"Let me help."' in result

    def test_inject_with_values_sorted_by_priority(self) -> None:
        values = [
            Value(name="Clarity", description="Be clear", priority=5),
            Value(name="Speed", description="Be fast", priority=10),
            Value(name="Accuracy", description="Be correct", priority=7),
        ]
        persona = make_persona(values=values)
        result = persona.inject()

        assert "## Values" in result
        # Speed (10) should come before Accuracy (7) which should come before Clarity (5)
        speed_pos = result.find("Speed")
        accuracy_pos = result.find("Accuracy")
        clarity_pos = result.find("Clarity")
        assert speed_pos < accuracy_pos < clarity_pos

    def test_inject_with_behavioral_map(self) -> None:
        behavioral_map = {
            "greeting": BehavioralEntry(
                posture="Warm and welcoming",
                example="Hello! How can I assist you today?",
            ),
            "error_handling": BehavioralEntry(
                posture="Apologetic but solution-focused",
                example="I apologize for the confusion. Let me help fix that.",
            ),
        }
        persona = make_persona(behavioral_map=behavioral_map)
        result = persona.inject()

        assert "## Behavioral Guidelines" in result
        assert "### greeting" in result
        assert "**Posture**: Warm and welcoming" in result
        assert "**Example**: Hello! How can I assist you today?" in result
        assert "### error_handling" in result

    def test_inject_full_persona(self) -> None:
        """Test with all fields populated."""
        voice = Voice(
            tone=["direct"],
            dos=["Be brief"],
            donts=["Ramble"],
            example_phrases=["In short:"],
        )
        values = [
            Value(name="Clarity", description="Clear communication", priority=10),
        ]
        behavioral_map = {
            "default": BehavioralEntry(posture="Helpful", example="How can I help?"),
        }
        persona = make_persona(
            soul="You are a concise assistant.",
            voice=voice,
            values=values,
            behavioral_map=behavioral_map,
        )

        result = persona.inject(base="System context.")

        # Check order: base, soul, voice, values, behavioral
        base_pos = result.find("System context.")
        soul_pos = result.find("You are a concise assistant.")
        tone_pos = result.find("## Tone")
        values_pos = result.find("## Values")
        behavioral_pos = result.find("## Behavioral Guidelines")

        assert base_pos < soul_pos < tone_pos < values_pos < behavioral_pos

    def test_inject_empty_persona(self) -> None:
        """Empty persona should return empty string."""
        persona = make_persona()
        result = persona.inject()
        assert result == ""

    def test_inject_strips_whitespace(self) -> None:
        persona = make_persona(soul="  Soul with spaces.  ")
        result = persona.inject()
        assert result == "Soul with spaces."


class TestPersonaProperties:
    """Test Persona property accessors."""

    def test_soul_property(self) -> None:
        persona = make_persona(soul="Test soul")
        assert persona.soul == "Test soul"

    def test_voice_property(self) -> None:
        voice = Voice(tone=["friendly"])
        persona = make_persona(voice=voice)
        assert persona.voice.tone == ["friendly"]

    def test_values_property(self) -> None:
        values = [Value(name="Test", description="A test", priority=1)]
        persona = make_persona(values=values)
        assert len(persona.values) == 1
        assert persona.values[0].name == "Test"

    def test_behavioral_map_property(self) -> None:
        behavioral_map = {"key": BehavioralEntry(posture="Test", example="Example")}
        persona = make_persona(behavioral_map=behavioral_map)
        assert "key" in persona.behavioral_map
        assert persona.behavioral_map["key"].posture == "Test"
