# tests/schemas/syndarix/test_agent_type_schemas.py
"""
Tests for AgentType schema validation.
"""

import pytest
from pydantic import ValidationError

from app.schemas.syndarix import (
    AgentTypeCreate,
    AgentTypeUpdate,
)


class TestAgentTypeCreateValidation:
    """Tests for AgentTypeCreate schema validation."""

    def test_valid_agent_type_create(self, valid_agent_type_data):
        """Test creating agent type with valid data."""
        agent_type = AgentTypeCreate(**valid_agent_type_data)

        assert agent_type.name == "Backend Engineer"
        assert agent_type.slug == "backend-engineer"
        assert agent_type.personality_prompt == "You are an expert backend engineer."
        assert agent_type.primary_model == "claude-opus-4-5-20251101"

    def test_agent_type_create_defaults(self, valid_agent_type_data):
        """Test that defaults are applied correctly."""
        agent_type = AgentTypeCreate(**valid_agent_type_data)

        assert agent_type.expertise == []
        assert agent_type.fallback_models == []
        assert agent_type.model_params == {}
        assert agent_type.mcp_servers == []
        assert agent_type.tool_permissions == {}
        assert agent_type.is_active is True

    def test_agent_type_create_with_all_fields(self, valid_agent_type_data):
        """Test creating agent type with all optional fields."""
        agent_type = AgentTypeCreate(
            **valid_agent_type_data,
            description="Detailed description",
            expertise=["python", "fastapi"],
            fallback_models=["claude-sonnet-4-20250514"],
            model_params={"temperature": 0.7},
            mcp_servers=["gitea", "slack"],
            tool_permissions={"allowed": ["*"]},
            is_active=True,
        )

        assert agent_type.description == "Detailed description"
        assert agent_type.expertise == ["python", "fastapi"]
        assert agent_type.fallback_models == ["claude-sonnet-4-20250514"]

    def test_agent_type_create_name_empty_fails(self):
        """Test that empty name raises ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            AgentTypeCreate(
                name="",
                slug="valid-slug",
                personality_prompt="Test prompt",
                primary_model="claude-opus-4-5-20251101",
            )

        errors = exc_info.value.errors()
        assert any("name" in str(e) for e in errors)

    def test_agent_type_create_name_stripped(self):
        """Test that name is stripped of whitespace."""
        agent_type = AgentTypeCreate(
            name="  Padded Name  ",
            slug="padded-slug",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
        )

        assert agent_type.name == "Padded Name"

    def test_agent_type_create_personality_prompt_required(self):
        """Test that personality_prompt is required."""
        with pytest.raises(ValidationError) as exc_info:
            AgentTypeCreate(
                name="Test Agent",
                slug="test-agent",
                primary_model="claude-opus-4-5-20251101",
            )

        errors = exc_info.value.errors()
        assert any("personality_prompt" in str(e).lower() for e in errors)

    def test_agent_type_create_primary_model_required(self):
        """Test that primary_model is required."""
        with pytest.raises(ValidationError) as exc_info:
            AgentTypeCreate(
                name="Test Agent",
                slug="test-agent",
                personality_prompt="Test prompt",
            )

        errors = exc_info.value.errors()
        assert any("primary_model" in str(e).lower() for e in errors)


class TestAgentTypeSlugValidation:
    """Tests for AgentType slug validation."""

    def test_valid_slugs(self):
        """Test various valid slug formats."""
        valid_slugs = [
            "simple",
            "with-hyphens",
            "has123numbers",
        ]

        for slug in valid_slugs:
            agent_type = AgentTypeCreate(
                name="Test Agent",
                slug=slug,
                personality_prompt="Test",
                primary_model="claude-opus-4-5-20251101",
            )
            assert agent_type.slug == slug

    def test_invalid_slug_uppercase(self):
        """Test that uppercase letters in slug raise ValidationError."""
        with pytest.raises(ValidationError):
            AgentTypeCreate(
                name="Test Agent",
                slug="Invalid-Uppercase",
                personality_prompt="Test",
                primary_model="claude-opus-4-5-20251101",
            )

    def test_invalid_slug_special_chars(self):
        """Test that special characters raise ValidationError."""
        with pytest.raises(ValidationError):
            AgentTypeCreate(
                name="Test Agent",
                slug="has_underscore",
                personality_prompt="Test",
                primary_model="claude-opus-4-5-20251101",
            )


class TestAgentTypeExpertiseValidation:
    """Tests for AgentType expertise validation."""

    def test_expertise_normalized_lowercase(self):
        """Test that expertise is normalized to lowercase."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            expertise=["Python", "FastAPI", "PostgreSQL"],
        )

        assert agent_type.expertise == ["python", "fastapi", "postgresql"]

    def test_expertise_stripped(self):
        """Test that expertise items are stripped."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            expertise=["  python  ", "  fastapi  "],
        )

        assert agent_type.expertise == ["python", "fastapi"]

    def test_expertise_empty_strings_removed(self):
        """Test that empty expertise strings are removed."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            expertise=["python", "", "  ", "fastapi"],
        )

        assert agent_type.expertise == ["python", "fastapi"]


class TestAgentTypeMcpServersValidation:
    """Tests for AgentType MCP servers validation."""

    def test_mcp_servers_stripped(self):
        """Test that MCP server names are stripped."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            mcp_servers=["  gitea  ", "  slack  "],
        )

        assert agent_type.mcp_servers == ["gitea", "slack"]

    def test_mcp_servers_empty_strings_removed(self):
        """Test that empty MCP server strings are removed."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            mcp_servers=["gitea", "", "  ", "slack"],
        )

        assert agent_type.mcp_servers == ["gitea", "slack"]


class TestAgentTypeUpdateValidation:
    """Tests for AgentTypeUpdate schema validation."""

    def test_agent_type_update_partial(self):
        """Test updating only some fields."""
        update = AgentTypeUpdate(
            name="Updated Name",
        )

        assert update.name == "Updated Name"
        assert update.slug is None
        assert update.description is None
        assert update.expertise is None

    def test_agent_type_update_all_fields(self):
        """Test updating all fields."""
        update = AgentTypeUpdate(
            name="Updated Name",
            slug="updated-slug",
            description="Updated description",
            expertise=["new-skill"],
            personality_prompt="Updated prompt",
            primary_model="new-model",
            fallback_models=["fallback-1"],
            model_params={"temp": 0.5},
            mcp_servers=["server-1"],
            tool_permissions={"key": "value"},
            is_active=False,
        )

        assert update.name == "Updated Name"
        assert update.slug == "updated-slug"
        assert update.is_active is False

    def test_agent_type_update_empty_name_fails(self):
        """Test that empty name in update raises ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            AgentTypeUpdate(name="")

        errors = exc_info.value.errors()
        assert any("name" in str(e) for e in errors)

    def test_agent_type_update_slug_validation(self):
        """Test that slug validation applies to updates."""
        with pytest.raises(ValidationError):
            AgentTypeUpdate(slug="Invalid-Slug")

    def test_agent_type_update_expertise_normalized(self):
        """Test that expertise is normalized in updates."""
        update = AgentTypeUpdate(
            expertise=["Python", "FastAPI"],
        )

        assert update.expertise == ["python", "fastapi"]


class TestAgentTypeJsonFields:
    """Tests for AgentType JSON field validation."""

    def test_model_params_complex(self):
        """Test complex model_params structure."""
        params = {
            "temperature": 0.7,
            "max_tokens": 4096,
            "top_p": 0.9,
            "stop_sequences": ["###"],
        }
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            model_params=params,
        )

        assert agent_type.model_params == params

    def test_tool_permissions_complex(self):
        """Test complex tool_permissions structure."""
        permissions = {
            "allowed": ["file:read", "git:commit"],
            "denied": ["file:delete"],
            "require_approval": ["git:push"],
        }
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            tool_permissions=permissions,
        )

        assert agent_type.tool_permissions == permissions

    def test_fallback_models_list(self):
        """Test fallback_models as a list."""
        models = ["claude-sonnet-4-20250514", "gpt-4o", "mistral-large"]
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            fallback_models=models,
        )

        assert agent_type.fallback_models == models


class TestAgentTypeCategoryFieldsValidation:
    """Tests for AgentType category and display field validation."""

    def test_valid_category_values(self):
        """Test that all valid category values are accepted."""
        valid_categories = [
            "development",
            "design",
            "quality",
            "operations",
            "ai_ml",
            "data",
            "leadership",
            "domain_expert",
        ]

        for category in valid_categories:
            agent_type = AgentTypeCreate(
                name="Test Agent",
                slug="test-agent",
                personality_prompt="Test",
                primary_model="claude-opus-4-5-20251101",
                category=category,
            )
            assert agent_type.category.value == category

    def test_category_null_allowed(self):
        """Test that null category is allowed."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            category=None,
        )
        assert agent_type.category is None

    def test_invalid_category_rejected(self):
        """Test that invalid category values are rejected."""
        with pytest.raises(ValidationError):
            AgentTypeCreate(
                name="Test Agent",
                slug="test-agent",
                personality_prompt="Test",
                primary_model="claude-opus-4-5-20251101",
                category="invalid_category",
            )

    def test_valid_hex_color(self):
        """Test that valid hex colors are accepted."""
        valid_colors = ["#3B82F6", "#EC4899", "#10B981", "#ffffff", "#000000"]

        for color in valid_colors:
            agent_type = AgentTypeCreate(
                name="Test Agent",
                slug="test-agent",
                personality_prompt="Test",
                primary_model="claude-opus-4-5-20251101",
                color=color,
            )
            assert agent_type.color == color

    def test_invalid_hex_color_rejected(self):
        """Test that invalid hex colors are rejected."""
        invalid_colors = [
            "not-a-color",
            "3B82F6",  # Missing #
            "#3B82F",  # Too short
            "#3B82F6A",  # Too long
            "#GGGGGG",  # Invalid hex chars
            "rgb(59, 130, 246)",  # RGB format not supported
        ]

        for color in invalid_colors:
            with pytest.raises(ValidationError):
                AgentTypeCreate(
                    name="Test Agent",
                    slug="test-agent",
                    personality_prompt="Test",
                    primary_model="claude-opus-4-5-20251101",
                    color=color,
                )

    def test_color_null_allowed(self):
        """Test that null color is allowed."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            color=None,
        )
        assert agent_type.color is None

    def test_sort_order_valid_range(self):
        """Test that valid sort_order values are accepted."""
        for sort_order in [0, 1, 500, 1000]:
            agent_type = AgentTypeCreate(
                name="Test Agent",
                slug="test-agent",
                personality_prompt="Test",
                primary_model="claude-opus-4-5-20251101",
                sort_order=sort_order,
            )
            assert agent_type.sort_order == sort_order

    def test_sort_order_default_zero(self):
        """Test that sort_order defaults to 0."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
        )
        assert agent_type.sort_order == 0

    def test_sort_order_negative_rejected(self):
        """Test that negative sort_order is rejected."""
        with pytest.raises(ValidationError):
            AgentTypeCreate(
                name="Test Agent",
                slug="test-agent",
                personality_prompt="Test",
                primary_model="claude-opus-4-5-20251101",
                sort_order=-1,
            )

    def test_sort_order_exceeds_max_rejected(self):
        """Test that sort_order > 1000 is rejected."""
        with pytest.raises(ValidationError):
            AgentTypeCreate(
                name="Test Agent",
                slug="test-agent",
                personality_prompt="Test",
                primary_model="claude-opus-4-5-20251101",
                sort_order=1001,
            )

    def test_icon_max_length(self):
        """Test that icon field respects max length."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            icon="x" * 50,
        )
        assert len(agent_type.icon) == 50

    def test_icon_exceeds_max_length_rejected(self):
        """Test that icon exceeding max length is rejected."""
        with pytest.raises(ValidationError):
            AgentTypeCreate(
                name="Test Agent",
                slug="test-agent",
                personality_prompt="Test",
                primary_model="claude-opus-4-5-20251101",
                icon="x" * 51,
            )


class TestAgentTypeTypicalTasksValidation:
    """Tests for typical_tasks field validation."""

    def test_typical_tasks_list(self):
        """Test typical_tasks as a list."""
        tasks = ["Write code", "Review PRs", "Debug issues"]
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            typical_tasks=tasks,
        )
        assert agent_type.typical_tasks == tasks

    def test_typical_tasks_default_empty(self):
        """Test typical_tasks defaults to empty list."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
        )
        assert agent_type.typical_tasks == []

    def test_typical_tasks_strips_whitespace(self):
        """Test that typical_tasks items are stripped."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            typical_tasks=["  Write code  ", "  Debug  "],
        )
        assert agent_type.typical_tasks == ["Write code", "Debug"]

    def test_typical_tasks_removes_empty_strings(self):
        """Test that empty strings are removed from typical_tasks."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            typical_tasks=["Write code", "", "  ", "Debug"],
        )
        assert agent_type.typical_tasks == ["Write code", "Debug"]


class TestAgentTypeCollaborationHintsValidation:
    """Tests for collaboration_hints field validation."""

    def test_collaboration_hints_list(self):
        """Test collaboration_hints as a list."""
        hints = ["backend-engineer", "qa-engineer"]
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            collaboration_hints=hints,
        )
        assert agent_type.collaboration_hints == hints

    def test_collaboration_hints_default_empty(self):
        """Test collaboration_hints defaults to empty list."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
        )
        assert agent_type.collaboration_hints == []

    def test_collaboration_hints_normalized_lowercase(self):
        """Test that collaboration_hints are normalized to lowercase."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            collaboration_hints=["Backend-Engineer", "QA-ENGINEER"],
        )
        assert agent_type.collaboration_hints == ["backend-engineer", "qa-engineer"]

    def test_collaboration_hints_strips_whitespace(self):
        """Test that collaboration_hints are stripped."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            collaboration_hints=["  backend-engineer  ", "  qa-engineer  "],
        )
        assert agent_type.collaboration_hints == ["backend-engineer", "qa-engineer"]

    def test_collaboration_hints_removes_empty_strings(self):
        """Test that empty strings are removed from collaboration_hints."""
        agent_type = AgentTypeCreate(
            name="Test Agent",
            slug="test-agent",
            personality_prompt="Test",
            primary_model="claude-opus-4-5-20251101",
            collaboration_hints=["backend-engineer", "", "  ", "qa-engineer"],
        )
        assert agent_type.collaboration_hints == ["backend-engineer", "qa-engineer"]


class TestAgentTypeUpdateCategoryFields:
    """Tests for AgentTypeUpdate category and display fields."""

    def test_update_category_field(self):
        """Test updating category field."""
        update = AgentTypeUpdate(category="ai_ml")
        assert update.category.value == "ai_ml"

    def test_update_icon_field(self):
        """Test updating icon field."""
        update = AgentTypeUpdate(icon="brain")
        assert update.icon == "brain"

    def test_update_color_field(self):
        """Test updating color field."""
        update = AgentTypeUpdate(color="#8B5CF6")
        assert update.color == "#8B5CF6"

    def test_update_sort_order_field(self):
        """Test updating sort_order field."""
        update = AgentTypeUpdate(sort_order=50)
        assert update.sort_order == 50

    def test_update_typical_tasks_field(self):
        """Test updating typical_tasks field."""
        update = AgentTypeUpdate(typical_tasks=["New task"])
        assert update.typical_tasks == ["New task"]

    def test_update_typical_tasks_strips_whitespace(self):
        """Test that typical_tasks are stripped on update."""
        update = AgentTypeUpdate(typical_tasks=["  New task  "])
        assert update.typical_tasks == ["New task"]

    def test_update_collaboration_hints_field(self):
        """Test updating collaboration_hints field."""
        update = AgentTypeUpdate(collaboration_hints=["new-collaborator"])
        assert update.collaboration_hints == ["new-collaborator"]

    def test_update_collaboration_hints_normalized(self):
        """Test that collaboration_hints are normalized on update."""
        update = AgentTypeUpdate(collaboration_hints=["  New-Collaborator  "])
        assert update.collaboration_hints == ["new-collaborator"]

    def test_update_invalid_color_rejected(self):
        """Test that invalid color is rejected on update."""
        with pytest.raises(ValidationError):
            AgentTypeUpdate(color="invalid")

    def test_update_invalid_sort_order_rejected(self):
        """Test that invalid sort_order is rejected on update."""
        with pytest.raises(ValidationError):
            AgentTypeUpdate(sort_order=-1)
