# tests/crud/syndarix/test_agent_type_crud.py
"""
Tests for AgentType CRUD operations.
"""

import uuid

import pytest

from app.crud.syndarix import agent_type as agent_type_crud
from app.schemas.syndarix import AgentTypeCreate, AgentTypeUpdate


class TestAgentTypeCreate:
    """Tests for agent type creation."""

    @pytest.mark.asyncio
    async def test_create_agent_type_success(self, async_test_db):
        """Test successfully creating an agent type."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            agent_type_data = AgentTypeCreate(
                name="QA Engineer",
                slug="qa-engineer",
                description="Specialized in testing and quality assurance",
                expertise=["testing", "pytest", "playwright"],
                personality_prompt="You are an expert QA engineer...",
                primary_model="claude-opus-4-5-20251101",
                fallback_models=["claude-sonnet-4-20250514"],
                model_params={"temperature": 0.5},
                mcp_servers=["gitea"],
                tool_permissions={"allowed": ["*"]},
                is_active=True,
            )
            result = await agent_type_crud.create(session, obj_in=agent_type_data)

            assert result.id is not None
            assert result.name == "QA Engineer"
            assert result.slug == "qa-engineer"
            assert result.expertise == ["testing", "pytest", "playwright"]
            assert result.is_active is True

    @pytest.mark.asyncio
    async def test_create_agent_type_duplicate_slug_fails(self, async_test_db, test_agent_type_crud):
        """Test creating agent type with duplicate slug raises ValueError."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            agent_type_data = AgentTypeCreate(
                name="Duplicate Agent",
                slug=test_agent_type_crud.slug,  # Duplicate slug
                personality_prompt="Duplicate",
                primary_model="claude-opus-4-5-20251101",
            )

            with pytest.raises(ValueError) as exc_info:
                await agent_type_crud.create(session, obj_in=agent_type_data)

            assert "already exists" in str(exc_info.value).lower()

    @pytest.mark.asyncio
    async def test_create_agent_type_minimal_fields(self, async_test_db):
        """Test creating agent type with minimal required fields."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            agent_type_data = AgentTypeCreate(
                name="Minimal Agent",
                slug="minimal-agent",
                personality_prompt="You are an assistant.",
                primary_model="claude-opus-4-5-20251101",
            )
            result = await agent_type_crud.create(session, obj_in=agent_type_data)

            assert result.name == "Minimal Agent"
            assert result.expertise == []  # Default
            assert result.fallback_models == []  # Default
            assert result.is_active is True  # Default


class TestAgentTypeRead:
    """Tests for agent type read operations."""

    @pytest.mark.asyncio
    async def test_get_agent_type_by_id(self, async_test_db, test_agent_type_crud):
        """Test getting agent type by ID."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            result = await agent_type_crud.get(session, id=str(test_agent_type_crud.id))

            assert result is not None
            assert result.id == test_agent_type_crud.id
            assert result.name == test_agent_type_crud.name

    @pytest.mark.asyncio
    async def test_get_agent_type_by_id_not_found(self, async_test_db):
        """Test getting non-existent agent type returns None."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            result = await agent_type_crud.get(session, id=str(uuid.uuid4()))
            assert result is None

    @pytest.mark.asyncio
    async def test_get_agent_type_by_slug(self, async_test_db, test_agent_type_crud):
        """Test getting agent type by slug."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            result = await agent_type_crud.get_by_slug(session, slug=test_agent_type_crud.slug)

            assert result is not None
            assert result.slug == test_agent_type_crud.slug

    @pytest.mark.asyncio
    async def test_get_agent_type_by_slug_not_found(self, async_test_db):
        """Test getting non-existent slug returns None."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            result = await agent_type_crud.get_by_slug(session, slug="non-existent-agent")
            assert result is None


class TestAgentTypeUpdate:
    """Tests for agent type update operations."""

    @pytest.mark.asyncio
    async def test_update_agent_type_basic_fields(self, async_test_db, test_agent_type_crud):
        """Test updating basic agent type fields."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            agent_type = await agent_type_crud.get(session, id=str(test_agent_type_crud.id))

            update_data = AgentTypeUpdate(
                name="Updated Agent Name",
                description="Updated description",
            )
            result = await agent_type_crud.update(session, db_obj=agent_type, obj_in=update_data)

            assert result.name == "Updated Agent Name"
            assert result.description == "Updated description"

    @pytest.mark.asyncio
    async def test_update_agent_type_expertise(self, async_test_db, test_agent_type_crud):
        """Test updating agent type expertise."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            agent_type = await agent_type_crud.get(session, id=str(test_agent_type_crud.id))

            update_data = AgentTypeUpdate(
                expertise=["new-skill", "another-skill"],
            )
            result = await agent_type_crud.update(session, db_obj=agent_type, obj_in=update_data)

            assert "new-skill" in result.expertise

    @pytest.mark.asyncio
    async def test_update_agent_type_model_params(self, async_test_db, test_agent_type_crud):
        """Test updating agent type model parameters."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            agent_type = await agent_type_crud.get(session, id=str(test_agent_type_crud.id))

            new_params = {"temperature": 0.9, "max_tokens": 8192}
            update_data = AgentTypeUpdate(model_params=new_params)
            result = await agent_type_crud.update(session, db_obj=agent_type, obj_in=update_data)

            assert result.model_params == new_params


class TestAgentTypeDelete:
    """Tests for agent type delete operations."""

    @pytest.mark.asyncio
    async def test_delete_agent_type(self, async_test_db):
        """Test deleting an agent type."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create an agent type to delete
        async with AsyncTestingSessionLocal() as session:
            agent_type_data = AgentTypeCreate(
                name="Delete Me Agent",
                slug="delete-me-agent",
                personality_prompt="Delete test",
                primary_model="claude-opus-4-5-20251101",
            )
            created = await agent_type_crud.create(session, obj_in=agent_type_data)
            agent_type_id = created.id

        # Delete the agent type
        async with AsyncTestingSessionLocal() as session:
            result = await agent_type_crud.remove(session, id=str(agent_type_id))
            assert result is not None

        # Verify deletion
        async with AsyncTestingSessionLocal() as session:
            deleted = await agent_type_crud.get(session, id=str(agent_type_id))
            assert deleted is None


class TestAgentTypeFilters:
    """Tests for agent type filtering and search."""

    @pytest.mark.asyncio
    async def test_get_multi_with_filters_active(self, async_test_db):
        """Test filtering agent types by is_active."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create active and inactive agent types
        async with AsyncTestingSessionLocal() as session:
            active_type = AgentTypeCreate(
                name="Active Agent Type",
                slug="active-agent-type-filter",
                personality_prompt="Active",
                primary_model="claude-opus-4-5-20251101",
                is_active=True,
            )
            await agent_type_crud.create(session, obj_in=active_type)

            inactive_type = AgentTypeCreate(
                name="Inactive Agent Type",
                slug="inactive-agent-type-filter",
                personality_prompt="Inactive",
                primary_model="claude-opus-4-5-20251101",
                is_active=False,
            )
            await agent_type_crud.create(session, obj_in=inactive_type)

        async with AsyncTestingSessionLocal() as session:
            active_types, _ = await agent_type_crud.get_multi_with_filters(
                session,
                is_active=True,
            )

            assert all(at.is_active for at in active_types)

    @pytest.mark.asyncio
    async def test_get_multi_with_filters_search(self, async_test_db):
        """Test searching agent types by name."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            agent_type_data = AgentTypeCreate(
                name="Searchable Agent Type",
                slug="searchable-agent-type",
                description="This is searchable",
                personality_prompt="Searchable",
                primary_model="claude-opus-4-5-20251101",
            )
            await agent_type_crud.create(session, obj_in=agent_type_data)

        async with AsyncTestingSessionLocal() as session:
            agent_types, total = await agent_type_crud.get_multi_with_filters(
                session,
                search="Searchable",
            )

            assert total >= 1
            assert any(at.name == "Searchable Agent Type" for at in agent_types)

    @pytest.mark.asyncio
    async def test_get_multi_with_filters_pagination(self, async_test_db):
        """Test pagination of agent type results."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            for i in range(5):
                agent_type_data = AgentTypeCreate(
                    name=f"Page Agent Type {i}",
                    slug=f"page-agent-type-{i}",
                    personality_prompt=f"Page {i}",
                    primary_model="claude-opus-4-5-20251101",
                )
                await agent_type_crud.create(session, obj_in=agent_type_data)

        async with AsyncTestingSessionLocal() as session:
            page1, _total = await agent_type_crud.get_multi_with_filters(
                session,
                skip=0,
                limit=2,
            )

            assert len(page1) <= 2


class TestAgentTypeSpecialMethods:
    """Tests for special agent type CRUD methods."""

    @pytest.mark.asyncio
    async def test_deactivate_agent_type(self, async_test_db):
        """Test deactivating an agent type."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create an active agent type
        async with AsyncTestingSessionLocal() as session:
            agent_type_data = AgentTypeCreate(
                name="Deactivate Me",
                slug="deactivate-me-agent",
                personality_prompt="Deactivate",
                primary_model="claude-opus-4-5-20251101",
                is_active=True,
            )
            created = await agent_type_crud.create(session, obj_in=agent_type_data)
            agent_type_id = created.id

        # Deactivate
        async with AsyncTestingSessionLocal() as session:
            result = await agent_type_crud.deactivate(session, agent_type_id=agent_type_id)

            assert result is not None
            assert result.is_active is False

    @pytest.mark.asyncio
    async def test_deactivate_nonexistent_agent_type(self, async_test_db):
        """Test deactivating non-existent agent type returns None."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            result = await agent_type_crud.deactivate(session, agent_type_id=uuid.uuid4())
            assert result is None

    @pytest.mark.asyncio
    async def test_get_with_instance_count(self, async_test_db, test_agent_type_crud, test_agent_instance_crud):
        """Test getting agent type with instance count."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            result = await agent_type_crud.get_with_instance_count(
                session,
                agent_type_id=test_agent_type_crud.id,
            )

            assert result is not None
            assert result["agent_type"].id == test_agent_type_crud.id
            assert result["instance_count"] >= 1

    @pytest.mark.asyncio
    async def test_get_with_instance_count_not_found(self, async_test_db):
        """Test getting non-existent agent type with count returns None."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        async with AsyncTestingSessionLocal() as session:
            result = await agent_type_crud.get_with_instance_count(
                session,
                agent_type_id=uuid.uuid4(),
            )
            assert result is None
