import pytest
from pydantic import BaseModel, Field, ValidationError

from src.broadie.schemas import AgentSchema, ChannelSchema, ChannelType, ModelProvider, ModelSchema, SubAgentSchema


class SubAgentOutput(BaseModel):
    """Test output schema for SubAgent with result field."""

    result: str = Field(description="The result of the operation")


class AgentOutput(BaseModel):
    """Test output schema for Agent."""


class TestModelProvider:
    """Test cases for ModelProvider enum."""

    def test_model_provider_values(self):
        """Test all ModelProvider enum values."""
        assert ModelProvider.google == "google_vertexai"
        assert ModelProvider.openai == "openai"
        assert ModelProvider.anthropic == "anthropic"
        assert ModelProvider.ollama == "ollama"
        assert ModelProvider.custom == "custom"

    def test_model_provider_string_comparison(self):
        """Test ModelProvider string comparison."""
        assert ModelProvider.google == "google_vertexai"
        assert str(ModelProvider.google.lower()) == "google_vertexai"

    def test_model_provider_all_values(self):
        """Test all enum values are accessible."""
        expected_values = {"google_vertexai", "openai", "anthropic", "ollama", "custom"}
        actual_values = {provider.value for provider in ModelProvider}
        assert actual_values == expected_values


class TestModelSchema:
    """Test cases for ModelSchema."""

    def test_model_schema_defaults(self):
        """Test ModelSchema default values."""
        model = ModelSchema()
        assert model.provider == ModelProvider.google
        assert model.name == "gemini-2.0-flash"
        assert model.settings == {}

    def test_model_schema_custom_values(self):
        """Test ModelSchema with custom values."""
        settings = {"temperature": 0.5, "max_tokens": 1000}
        model = ModelSchema(
            provider=ModelProvider.openai,
            name="gpt-4",
            settings=settings,
        )
        assert model.provider == ModelProvider.openai
        assert model.name == "gpt-4"
        assert model.settings == settings

    def test_model_schema_provider_validation(self):
        """Test ModelSchema provider validation."""
        # Valid provider
        model = ModelSchema(provider=ModelProvider.anthropic)
        assert model.provider == ModelProvider.anthropic

        # Invalid provider should raise ValidationError
        with pytest.raises(ValidationError):
            ModelSchema(provider="invalid_provider")

    def test_model_schema_json_serialization(self):
        """Test ModelSchema JSON serialization."""
        model = ModelSchema(
            provider=ModelProvider.openai,
            name="gpt-4",
            settings={"temperature": 0.7},
        )
        json_data = model.model_dump()
        assert json_data["provider"] == "openai"
        assert json_data["name"] == "gpt-4"
        assert json_data["settings"] == {"temperature": 0.7}


class TestChannelType:
    """Test cases for ChannelType enum."""

    def test_channel_type_values(self):
        """Test all ChannelType enum values."""
        assert ChannelType.slack == "slack"
        assert ChannelType.api == "api"
        assert ChannelType.email == "email"

    def test_channel_type_all_values(self):
        """Test all enum values are accessible."""
        expected_values = {"slack", "api", "email"}
        actual_values = {channel_type.value for channel_type in ChannelType}
        assert actual_values == expected_values


class TestChannelSchema:
    """Test cases for ChannelSchema."""

    def test_channel_schema_required_field(self):
        """Test ChannelSchema with required field only."""
        channel = ChannelSchema(type=ChannelType.slack)
        assert channel.type == ChannelType.slack
        assert channel.target is None
        assert channel.instructions is None
        assert channel.enabled is True

    def test_channel_schema_all_fields(self):
        """Test ChannelSchema with all fields."""
        channel = ChannelSchema(
            type=ChannelType.email,
            target="user@example.com",
            instructions="Send daily reports",
            enabled=False,
        )
        assert channel.type == ChannelType.email
        assert channel.target == "user@example.com"
        assert channel.instructions == "Send daily reports"
        assert channel.enabled is False

    def test_channel_schema_invalid_type(self):
        """Test ChannelSchema with invalid channel type."""
        with pytest.raises(ValidationError):
            ChannelSchema(type="invalid_channel")

    def test_channel_schema_defaults(self):
        """Test ChannelSchema default values."""
        channel = ChannelSchema(type=ChannelType.api)
        assert channel.target is None
        assert channel.instructions is None
        assert channel.enabled is True


class TestSubAgentSchema:
    """Test cases for SubAgentSchema."""

    def test_subagent_schema_minimal(self):
        """Test SubAgentSchema with minimal required fields."""
        subagent = SubAgentSchema(name="test_agent", prompt="Be helpful")
        assert subagent.name == "test_agent"
        assert subagent.prompt == "Be helpful"
        assert subagent.description is None  # Schema doesn't auto-default, factory does
        assert subagent.tools == []
        assert subagent.model is None
        assert subagent.output_schema.__name__ == "BaseDefaultOutput"
        assert subagent.strict_mode is True

    def test_subagent_schema_full(self):
        """Test SubAgentSchema with all fields."""
        model = ModelSchema(provider=ModelProvider.google, name="gemini-2.0-flash")

        def dummy_tool():
            """A dummy tool."""
            return "result"

        tools = [dummy_tool]
        output_schema = SubAgentOutput

        subagent = SubAgentSchema(
            name="assistant",
            description="Helpful assistant",
            prompt="You are a helpful assistant",
            tools=tools,
            model=model,
            output_schema=output_schema,
            strict_mode=False,
        )

        assert subagent.name == "assistant"
        assert subagent.description == "Helpful assistant"
        assert subagent.prompt == "You are a helpful assistant"
        assert len(subagent.tools) == 1
        assert callable(subagent.tools[0])
        assert subagent.model == model
        assert subagent.output_schema == output_schema
        assert subagent.strict_mode is False

    def test_subagent_schema_validation(self):
        """Test SubAgentSchema field validation."""
        # Missing required name field
        with pytest.raises(ValidationError):
            SubAgentSchema()

    def test_subagent_schema_with_callable_tools(self):
        """Test SubAgentSchema accepts callable tools."""

        def tool1():
            """Tool 1."""
            return "result"

        def tool2():
            """Tool 2."""
            return "result"

        subagent = SubAgentSchema(name="test", prompt="Help", tools=[tool1, tool2])

        assert len(subagent.tools) == 2
        assert callable(subagent.tools[0])
        assert callable(subagent.tools[1])


class TestAgentSchema:
    """Test cases for AgentSchema."""

    def test_agent_schema_minimal(self):
        """Test AgentSchema with minimal required fields."""
        agent = AgentSchema(name="agent", instruction="Be helpful")
        assert agent.name == "agent"
        assert agent.description is None
        assert agent.instruction == "Be helpful"
        assert isinstance(agent.model, ModelSchema)
        assert agent.tools == []
        assert agent.temperature == 0.2
        assert agent.max_tokens == 50000
        assert agent.max_retries == 2
        assert agent.subagents == []
        assert agent.output_schema.__name__ == "BaseDefaultOutput"
        assert agent.strict_mode is True
        assert agent.channels == []

    def test_agent_schema_full(self):
        """Test AgentSchema with all fields."""
        model = ModelSchema(provider=ModelProvider.google, name="gemini-2.0-flash")
        subagent = SubAgentSchema(name="helper", prompt="Be helpful", description="Helper agent")
        channel = ChannelSchema(type=ChannelType.slack, target="#general")

        def dummy_tool():
            """A dummy tool."""
            return "result"

        tools = [dummy_tool]
        output_schema = AgentOutput

        agent = AgentSchema(
            name="main_agent",
            description="Main processing agent",
            instruction="Process requests efficiently",
            model=model,
            tools=tools,
            temperature=0.8,
            max_tokens=100000,
            max_retries=5,
            subagents=[subagent],
            output_schema=output_schema,
            strict_mode=False,
            channels=[channel],
        )

        assert agent.name == "main_agent"
        assert agent.description == "Main processing agent"
        assert agent.instruction == "Process requests efficiently"
        assert agent.model == model
        assert len(agent.tools) == 1
        assert callable(agent.tools[0])
        assert agent.temperature == 0.8
        assert agent.max_tokens == 100000
        assert agent.max_retries == 5
        assert len(agent.subagents) == 1
        assert agent.subagents[0] == subagent
        assert agent.output_schema == output_schema
        assert agent.strict_mode is False
        assert len(agent.channels) == 1
        assert agent.channels[0] == channel

    def test_agent_schema_validation_errors(self):
        """Test AgentSchema validation errors."""
        # Missing required name
        with pytest.raises(ValidationError):
            AgentSchema(instruction="Be helpful")

        # Missing required instruction
        with pytest.raises(ValidationError):
            AgentSchema(name="agent")

        # Invalid temperature type
        with pytest.raises(ValidationError):
            AgentSchema(name="agent", instruction="help", temperature="high")

        # Invalid max_tokens type
        with pytest.raises(ValidationError):
            AgentSchema(name="agent", instruction="help", max_tokens="many")

    def test_agent_schema_nested_validation(self):
        """Test AgentSchema with invalid nested objects."""
        # Invalid subagent
        with pytest.raises(ValidationError):
            AgentSchema(
                name="agent",
                instruction="help",
                subagents=[{"invalid": "subagent"}],
            )

        # Invalid channel
        with pytest.raises(ValidationError):
            AgentSchema(
                name="agent",
                instruction="help",
                channels=[{"invalid": "channel"}],
            )

    def test_agent_schema_default_model(self):
        """Test AgentSchema default model creation."""
        agent = AgentSchema(name="test", instruction="help")
        assert isinstance(agent.model, ModelSchema)
        assert agent.model.provider == ModelProvider.google
        assert agent.model.name == "gemini-2.0-flash"

    def test_agent_schema_json_serialization(self):
        """Test AgentSchema complete JSON serialization."""
        agent = AgentSchema(
            name="test_agent",
            instruction="Test instruction",
            temperature=0.5,
        )
        json_data = agent.model_dump()

        assert json_data["name"] == "test_agent"
        assert json_data["instruction"] == "Test instruction"
        assert json_data["temperature"] == 0.5
        assert "model" in json_data
        assert json_data["model"]["provider"] == "google_vertexai"
