"""Tests for Orchestrator + MessageBus integration (U7)."""

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

from agentkit.bus.message import AgentMessage
from agentkit.bus.memory_bus import InMemoryMessageBus
from agentkit.core.orchestrator import Orchestrator, OrchestratorConfig
from agentkit.core.protocol import TaskMessage, TaskStatus


def _make_task(**overrides) -> TaskMessage:
    defaults = {
        "task_id": "task-001",
        "agent_name": "test_agent",
        "task_type": "analyze",
        "priority": 0,
        "input_data": {"query": "test"},
        "callback_url": None,
        "created_at": datetime.now(timezone.utc),
        "timeout_seconds": 60,
    }
    defaults.update(overrides)
    return TaskMessage(**defaults)


def _make_mock_pool():
    """Create a mock AgentPool with a working agent."""
    mock_agent = AsyncMock()
    mock_result = MagicMock()
    mock_result.output_data = {"result": "done"}
    mock_agent.execute = AsyncMock(return_value=mock_result)

    pool = MagicMock()
    pool.get_agent = lambda name: mock_agent
    pool.list_agents = lambda: [
        {"name": "test_agent", "agent_type": "worker", "description": "Test agent"}
    ]
    return pool


class TestOrchestratorWithMessageBus:
    @pytest.mark.asyncio
    async def test_worker_publishes_progress(self):
        """Worker should publish progress via MessageBus after execution."""
        bus = InMemoryMessageBus()
        pool = _make_mock_pool()
        orch = Orchestrator(agent_pool=pool, message_bus=bus)

        # Subscribe orchestrator to receive progress
        progress_messages: list[AgentMessage] = []
        await bus.subscribe("orchestrator", lambda msg: progress_messages.append(msg))

        task = _make_task()
        result = await orch.execute(task)
        assert result.status == TaskStatus.COMPLETED

        # Give consumer task time to process
        await asyncio.sleep(0.2)
        assert len(progress_messages) >= 1
        assert progress_messages[0].topic == "task.progress"

    @pytest.mark.asyncio
    async def test_no_message_bus_works_normally(self):
        """Without MessageBus, Orchestrator should work normally."""
        pool = _make_mock_pool()
        orch = Orchestrator(agent_pool=pool)

        task = _make_task()
        result = await orch.execute(task)
        assert result.status == TaskStatus.COMPLETED

    @pytest.mark.asyncio
    async def test_message_bus_injected_via_config(self):
        """MessageBus should be injectable via constructor."""
        bus = InMemoryMessageBus()
        pool = _make_mock_pool()
        config = OrchestratorConfig(adaptive=True)
        orch = Orchestrator(
            agent_pool=pool,
            message_bus=bus,
            config=config,
        )
        assert orch._message_bus is bus


class TestAgentPoolWithMessageBus:
    @pytest.mark.asyncio
    async def test_agent_registered_to_bus_on_create(self):
        """Agent should be registered to MessageBus when created."""
        from agentkit.core.agent_pool import AgentPool
        from agentkit.llm.gateway import LLMGateway
        from agentkit.skills.registry import SkillRegistry

        bus = InMemoryMessageBus()
        pool = AgentPool(
            llm_gateway=MagicMock(spec=LLMGateway),
            skill_registry=SkillRegistry(),
            message_bus=bus,
        )

        # Verify bus has the message_bus reference
        assert pool._message_bus is bus

    @pytest.mark.asyncio
    async def test_pool_without_bus_works(self):
        """AgentPool without MessageBus should work normally."""
        from agentkit.core.agent_pool import AgentPool
        from agentkit.llm.gateway import LLMGateway
        from agentkit.skills.registry import SkillRegistry

        pool = AgentPool(
            llm_gateway=MagicMock(spec=LLMGateway),
            skill_registry=SkillRegistry(),
        )
        assert pool._message_bus is None
