"""
Farnsworth Agent System Tests

Comprehensive tests for:
- Agent base functionality
- Specialist agents (Code, Reasoning, Research, Creative)
- Swarm orchestration
- Multi-agent coordination
- User avatar modeling
"""

import asyncio
import pytest
from unittest.mock import AsyncMock, MagicMock


@pytest.fixture
def event_loop():
    """Create event loop for async tests."""
    loop = asyncio.new_event_loop()
    yield loop
    loop.close()


@pytest.fixture
def mock_llm_backend():
    """Create mock LLM backend for testing."""
    mock = AsyncMock()
    mock.generate = AsyncMock(return_value=AsyncMock(
        text="Mock LLM response",
        confidence=0.85,
        tokens_used=100,
    ))
    return mock


class TestBaseAgent:
    """Tests for base agent functionality."""

    def test_agent_creation(self):
        """Test agent initialization."""
        from farnsworth.agents.base_agent import BaseAgent, AgentCapability

        agent = BaseAgent(
            name="TestAgent",
            capabilities=[AgentCapability.REASONING],
        )

        assert agent.name == "TestAgent"
        assert AgentCapability.REASONING in agent.capabilities
        assert agent.agent_id is not None

    def test_agent_status(self):
        """Test agent status reporting."""
        from farnsworth.agents.base_agent import BaseAgent, AgentStatus

        agent = BaseAgent(name="TestAgent")

        assert agent.status == AgentStatus.IDLE

        status = agent.get_status()
        assert "name" in status
        assert "status" in status

    @pytest.mark.asyncio
    async def test_agent_execution(self, mock_llm_backend):
        """Test agent task execution."""
        from farnsworth.agents.base_agent import BaseAgent

        agent = BaseAgent(name="TestAgent", llm_backend=mock_llm_backend)

        result = await agent.execute("Test task")

        assert result is not None
        assert hasattr(result, "success")

    def test_capability_check(self):
        """Test capability checking."""
        from farnsworth.agents.base_agent import BaseAgent, AgentCapability

        agent = BaseAgent(
            name="CodeAgent",
            capabilities=[AgentCapability.CODE_GENERATION, AgentCapability.CODE_ANALYSIS],
        )

        assert agent.has_capability(AgentCapability.CODE_GENERATION)
        assert not agent.has_capability(AgentCapability.CREATIVE_WRITING)


class TestSpecialistAgents:
    """Tests for specialist agent implementations."""

    @pytest.mark.asyncio
    async def test_code_agent_creation(self, mock_llm_backend):
        """Test code agent initialization."""
        from farnsworth.agents.specialist_agents import create_code_agent

        agent = create_code_agent(llm_backend=mock_llm_backend)

        assert agent is not None
        assert "code" in agent.name.lower()

    @pytest.mark.asyncio
    async def test_reasoning_agent_creation(self, mock_llm_backend):
        """Test reasoning agent initialization."""
        from farnsworth.agents.specialist_agents import create_reasoning_agent

        agent = create_reasoning_agent(llm_backend=mock_llm_backend)

        assert agent is not None
        assert "reason" in agent.name.lower()

    @pytest.mark.asyncio
    async def test_research_agent_creation(self, mock_llm_backend):
        """Test research agent initialization."""
        from farnsworth.agents.specialist_agents import create_research_agent

        agent = create_research_agent(llm_backend=mock_llm_backend)

        assert agent is not None
        assert "research" in agent.name.lower()

    @pytest.mark.asyncio
    async def test_creative_agent_creation(self, mock_llm_backend):
        """Test creative agent initialization."""
        from farnsworth.agents.specialist_agents import create_creative_agent

        agent = create_creative_agent(llm_backend=mock_llm_backend)

        assert agent is not None
        assert "creative" in agent.name.lower()

    @pytest.mark.asyncio
    async def test_code_agent_task(self, mock_llm_backend):
        """Test code agent executing a coding task."""
        from farnsworth.agents.specialist_agents import create_code_agent

        agent = create_code_agent(llm_backend=mock_llm_backend)

        result = await agent.execute("Write a function to calculate factorial")

        assert result is not None
        assert hasattr(result, "output")


class TestSwarmOrchestrator:
    """Tests for swarm orchestration."""

    def test_orchestrator_creation(self):
        """Test swarm orchestrator initialization."""
        from farnsworth.agents.swarm_orchestrator import SwarmOrchestrator

        orchestrator = SwarmOrchestrator(max_concurrent=5)

        assert orchestrator.max_concurrent == 5
        assert orchestrator.active_agent_count == 0

    @pytest.mark.asyncio
    async def test_agent_spawning(self, mock_llm_backend):
        """Test spawning agents from the orchestrator."""
        from farnsworth.agents.swarm_orchestrator import SwarmOrchestrator
        from farnsworth.agents.specialist_agents import create_code_agent

        orchestrator = SwarmOrchestrator()
        orchestrator.register_agent_factory("code", lambda: create_code_agent(llm_backend=mock_llm_backend))
        orchestrator.llm_backend = mock_llm_backend

        agent = await orchestrator.spawn_agent("code")

        assert agent is not None
        assert orchestrator.active_agent_count == 1

    @pytest.mark.asyncio
    async def test_task_submission(self, mock_llm_backend):
        """Test submitting tasks to the swarm."""
        from farnsworth.agents.swarm_orchestrator import SwarmOrchestrator

        orchestrator = SwarmOrchestrator()
        orchestrator.llm_backend = mock_llm_backend

        task_id = await orchestrator.submit_task(
            description="Test task",
            context={"key": "value"},
        )

        assert task_id is not None

    @pytest.mark.asyncio
    async def test_task_routing(self, mock_llm_backend):
        """Test automatic task routing to appropriate agents."""
        from farnsworth.agents.swarm_orchestrator import SwarmOrchestrator

        orchestrator = SwarmOrchestrator()
        orchestrator.llm_backend = mock_llm_backend

        # Code-related task should route to code agent
        agent_type = orchestrator._route_task("Write a Python function")
        assert agent_type == "code"

        # Reasoning task
        agent_type = orchestrator._route_task("Analyze this problem logically")
        assert agent_type == "reasoning"

    def test_swarm_status(self):
        """Test getting swarm status."""
        from farnsworth.agents.swarm_orchestrator import SwarmOrchestrator

        orchestrator = SwarmOrchestrator()
        status = orchestrator.get_swarm_status()

        assert "active_agents" in status
        assert "pending_tasks" in status

    @pytest.mark.asyncio
    async def test_handoff_protocol(self, mock_llm_backend):
        """Test agent handoff for complex tasks."""
        from farnsworth.agents.swarm_orchestrator import SwarmOrchestrator

        orchestrator = SwarmOrchestrator()
        orchestrator.llm_backend = mock_llm_backend

        # Simulate handoff scenario
        handoff_data = await orchestrator.create_handoff(
            from_agent="research",
            to_agent="code",
            context={"research_results": "sample data"},
        )

        assert handoff_data is not None


class TestMultiAgentCoordination:
    """Tests for multi-agent coordination."""

    @pytest.mark.asyncio
    async def test_parallel_execution(self, mock_llm_backend):
        """Test parallel task execution across agents."""
        from farnsworth.agents.swarm_orchestrator import SwarmOrchestrator

        orchestrator = SwarmOrchestrator(max_concurrent=3)
        orchestrator.llm_backend = mock_llm_backend

        tasks = [
            "Task 1: Research topic",
            "Task 2: Write code",
            "Task 3: Analyze results",
        ]

        results = await orchestrator.execute_parallel(tasks)

        assert len(results) == 3

    @pytest.mark.asyncio
    async def test_subtask_decomposition(self, mock_llm_backend):
        """Test decomposing main task into subtasks."""
        from farnsworth.agents.swarm_orchestrator import SwarmOrchestrator

        orchestrator = SwarmOrchestrator()
        orchestrator.llm_backend = mock_llm_backend

        main_task = "Build a web scraper that extracts data and saves to database"

        results = await orchestrator.execute_with_subtasks(
            main_task=main_task,
            subtasks=[
                "Research web scraping libraries",
                "Write the scraping code",
                "Implement database storage",
            ],
        )

        assert len(results) >= 1


class TestUserAvatar:
    """Tests for user avatar modeling."""

    def test_avatar_creation(self):
        """Test user avatar initialization."""
        from farnsworth.agents.user_avatar import UserAvatar

        avatar = UserAvatar(user_id="test_user")

        assert avatar.user_id == "test_user"
        assert avatar.preferences is not None

    @pytest.mark.asyncio
    async def test_preference_learning(self):
        """Test learning user preferences from interactions."""
        from farnsworth.agents.user_avatar import UserAvatar

        avatar = UserAvatar(user_id="test_user")

        # Record some interactions
        await avatar.record_interaction(
            action="code_request",
            feedback="positive",
            context={"language": "python"},
        )

        await avatar.record_interaction(
            action="explanation_request",
            feedback="positive",
            context={"detail_level": "detailed"},
        )

        # Check learned preferences
        prefs = avatar.get_preferences()
        assert prefs is not None

    @pytest.mark.asyncio
    async def test_response_personalization(self):
        """Test personalizing responses based on user model."""
        from farnsworth.agents.user_avatar import UserAvatar

        avatar = UserAvatar(user_id="test_user")

        # Set some preferences
        avatar.preferences = {
            "verbosity": 0.8,
            "code_preference": 0.9,
            "explanation_depth": 0.7,
        }

        suggestion = avatar.suggest_response_style("code generation task")

        assert suggestion is not None
        assert "temperature" in suggestion or "verbosity" in suggestion


class TestMetaCognition:
    """Tests for meta-cognition agent."""

    def test_metacognition_creation(self):
        """Test meta-cognition agent initialization."""
        from farnsworth.agents.meta_cognition import MetaCognitionAgent

        agent = MetaCognitionAgent()
        assert agent is not None

    @pytest.mark.asyncio
    async def test_self_reflection(self, mock_llm_backend):
        """Test self-reflection capability."""
        from farnsworth.agents.meta_cognition import MetaCognitionAgent

        agent = MetaCognitionAgent(llm_backend=mock_llm_backend)

        reflection = await agent.reflect_on_performance(
            task="Code generation",
            result={"success": True, "confidence": 0.7},
            feedback=None,
        )

        assert reflection is not None

    @pytest.mark.asyncio
    async def test_capability_gap_detection(self, mock_llm_backend):
        """Test detecting capability gaps."""
        from farnsworth.agents.meta_cognition import MetaCognitionAgent

        agent = MetaCognitionAgent(llm_backend=mock_llm_backend)

        gaps = await agent.detect_capability_gaps(
            failed_tasks=[
                {"task": "Advanced math proof", "error": "Unable to complete"},
                {"task": "Complex reasoning", "error": "Low confidence"},
            ]
        )

        assert isinstance(gaps, list)

    @pytest.mark.asyncio
    async def test_improvement_proposals(self, mock_llm_backend):
        """Test generating improvement proposals."""
        from farnsworth.agents.meta_cognition import MetaCognitionAgent

        agent = MetaCognitionAgent(llm_backend=mock_llm_backend)

        proposals = await agent.propose_improvements(
            current_metrics={
                "task_success": 0.75,
                "user_satisfaction": 0.8,
                "efficiency": 0.6,
            }
        )

        assert proposals is not None


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
