"""
Test suite for SubstrateAgent WITHOUT MCP integration.

This test suite ensures that SubstrateAgent works correctly when MCP
functionality is NOT used, verifying backward compatibility and that
MCP additions don't interfere with normal operations.

Tests cover:
- Agent initialization without MCP
- All built-in handlers work without MCP
- Custom handlers work without MCP
- LLM queries work without MCP tools
- No MCP dependencies are loaded when not used
- Error handling without MCP
"""
import pytest
import asyncio
from typing import Dict, Any

from daita.agents.substrate import SubstrateAgent
from daita.config.base import AgentConfig, RetryPolicy
from daita.llm.mock import MockLLMProvider


class TestSubstrateAgentWithoutMCP:
    """Test that SubstrateAgent works perfectly without MCP."""

    def test_initialization_no_mcp(self):
        """Test agent initializes correctly without MCP parameter."""
        agent = SubstrateAgent(name="No MCP Agent")

        assert agent.name == "No MCP Agent"
        assert agent.mcp_registry is None
        assert agent.mcp_tools == []
        assert agent._mcp_server_configs == []
        assert hasattr(agent, 'handlers')
        assert len(agent.handlers) > 0  # Built-in handlers should exist

    def test_initialization_with_llm_no_mcp(self):
        """Test agent with LLM but no MCP."""
        llm = MockLLMProvider()
        agent = SubstrateAgent(
            name="LLM Agent",
            llm_provider=llm,
            api_key="test_key"
        )

        assert agent.llm is not None
        assert agent.mcp_registry is None
        assert agent.mcp_tools == []

    def test_no_mcp_attributes_set(self):
        """Test MCP attributes are properly initialized even without MCP."""
        agent = SubstrateAgent()

        # These should exist but be empty/None
        assert hasattr(agent, 'mcp_registry')
        assert hasattr(agent, 'mcp_tools')
        assert hasattr(agent, '_mcp_server_configs')
        assert agent.mcp_registry is None
        assert isinstance(agent.mcp_tools, list)
        assert len(agent.mcp_tools) == 0


class TestBuiltInHandlersWithoutMCP:
    """Test all built-in handlers work without MCP."""

    @pytest.fixture
    def agent_with_llm(self):
        """Create agent with LLM but no MCP."""
        llm = MockLLMProvider()
        llm.set_response("default", "Test response from LLM")
        return SubstrateAgent(
            name="Test Agent",
            llm_provider=llm,
            api_key="test_key"
        )

    @pytest.fixture
    def agent_no_llm(self):
        """Create agent without LLM or MCP."""
        return SubstrateAgent(name="No LLM Agent")

    @pytest.mark.asyncio
    async def test_analyze_handler_no_mcp(self, agent_with_llm):
        """Test analyze handler works without MCP."""
        await agent_with_llm.start()

        test_data = {"sales": [100, 200], "region": "West"}
        result = await agent_with_llm.process("analyze", test_data)

        assert result["status"] == "success"
        assert "data_type" in result["result"]
        assert result["result"]["data_type"] == "dict"

        await agent_with_llm.stop()

    @pytest.mark.asyncio
    async def test_transform_handler_no_mcp(self, agent_no_llm):
        """Test transform handler works without LLM or MCP."""
        await agent_no_llm.start()

        result = await agent_no_llm.process("transform", {"key": "value"})

        assert result["status"] == "success"
        assert "transformed" in result["result"]

        await agent_no_llm.stop()

    @pytest.mark.asyncio
    async def test_process_data_handler_no_mcp(self, agent_no_llm):
        """Test process_data handler works without LLM or MCP."""
        await agent_no_llm.start()

        result = await agent_no_llm.process("process_data", [1, 2, 3])

        assert result["status"] == "success"
        assert result["result"]["task"] == "process_data"

        await agent_no_llm.stop()

    @pytest.mark.asyncio
    async def test_custom_handler_no_mcp(self, agent_no_llm):
        """Test custom handler works without MCP."""
        await agent_no_llm.start()

        result = await agent_no_llm.process("custom", {"test": "data"})

        assert result["status"] == "success"
        assert result["result"]["task"] == "custom"

        await agent_no_llm.stop()

    @pytest.mark.asyncio
    async def test_llm_query_handler_no_mcp(self, agent_with_llm):
        """Test LLM query handler works without MCP tools."""
        await agent_with_llm.start()

        result = await agent_with_llm.process("llm_query", "Test question")

        assert result["status"] == "success"
        assert "agent_response" in result["result"]
        assert "prompt_used" in result["result"]
        # Should NOT have MCP tools info
        assert "mcp_tools_available" not in result["result"]

        await agent_with_llm.stop()


class TestCustomHandlersWithoutMCP:
    """Test custom handlers work without MCP."""

    @pytest.mark.asyncio
    async def test_async_custom_handler_no_mcp(self):
        """Test async custom handler without MCP."""
        async def my_handler(data, context, agent):
            return {
                "processed": data,
                "agent_has_mcp": agent.mcp_registry is not None
            }

        agent = SubstrateAgent(handlers={"my_task": my_handler})
        await agent.start()

        result = await agent.process("my_task", {"input": "test"})

        assert result["status"] == "success"
        assert result["result"]["processed"] == {"input": "test"}
        assert result["result"]["agent_has_mcp"] is False

        await agent.stop()

    @pytest.mark.asyncio
    async def test_sync_custom_handler_no_mcp(self):
        """Test sync custom handler without MCP."""
        def my_sync_handler(data, context, agent):
            return {"sync": True, "data": data}

        agent = SubstrateAgent(handlers={"sync_task": my_sync_handler})
        await agent.start()

        result = await agent.process("sync_task", "test")

        assert result["status"] == "success"
        assert result["result"]["sync"] is True

        await agent.stop()


class TestMCPSetupSkipped:
    """Test that MCP setup is properly skipped when not configured."""

    @pytest.mark.asyncio
    async def test_no_mcp_setup_called(self):
        """Test that _setup_mcp_tools is not invoked when no MCP configured."""
        agent = SubstrateAgent(name="Test Agent")
        await agent.start()

        # Process multiple tasks
        await agent.process("custom", {"test": 1})
        await agent.process("process_data", {"test": 2})

        # MCP should never be initialized
        assert agent.mcp_registry is None
        assert len(agent.mcp_tools) == 0

        await agent.stop()

    @pytest.mark.asyncio
    async def test_mcp_setup_skipped_with_llm(self):
        """Test MCP setup skipped even with LLM configured."""
        llm = MockLLMProvider()
        agent = SubstrateAgent(
            name="LLM Agent",
            llm_provider=llm,
            api_key="test_key"
        )
        await agent.start()

        # LLM query should work without MCP
        result = await agent.process("llm_query", "Test")

        assert result["status"] == "success"
        assert agent.mcp_registry is None

        await agent.stop()


class TestRetryWithoutMCP:
    """Test retry functionality works without MCP."""

    @pytest.mark.asyncio
    async def test_retry_success_no_mcp(self):
        """Test successful retry without MCP."""
        config = AgentConfig(
            name="Retry Agent",
            enable_retry=True,
            retry_policy=RetryPolicy(max_retries=2, initial_delay=0.01)
        )

        call_count = 0

        async def flaky_handler(data, context, agent):
            nonlocal call_count
            call_count += 1
            if call_count == 1:
                raise Exception("Temporary failure")
            return {"success": True}

        agent = SubstrateAgent(
            config=config,
            handlers={"flaky": flaky_handler}
        )
        await agent.start()

        result = await agent.process("flaky", {})

        assert result["status"] == "success"
        assert call_count == 2  # Failed once, succeeded on retry

        await agent.stop()


class TestFocusWithoutMCP:
    """Test focus functionality works without MCP."""

    @pytest.mark.asyncio
    async def test_focus_application_no_mcp(self):
        """Test focus is applied correctly without MCP."""
        agent = SubstrateAgent(
            name="Focus Agent",
            focus=["name", "age"]
        )
        await agent.start()

        data = {"name": "John", "age": 30, "city": "NYC"}
        result = await agent.process("process_data", data)

        assert result["status"] == "success"

        await agent.stop()


class TestRelayWithoutMCP:
    """Test relay functionality works without MCP."""

    @pytest.mark.asyncio
    async def test_relay_publishing_no_mcp(self):
        """Test relay publishing works without MCP."""
        from unittest.mock import patch

        with patch('daita.core.relay.publish') as mock_publish:
            agent = SubstrateAgent(
                name="Relay Agent",
                relay="test_channel"
            )
            await agent.start()

            result = await agent.process("custom", {"test": "data"})

            assert result["status"] == "success"
            mock_publish.assert_called_once()

            await agent.stop()


class TestHealthWithoutMCP:
    """Test health property without MCP."""

    def test_health_no_mcp(self):
        """Test health property shows correct status without MCP."""
        agent = SubstrateAgent(name="Health Test")

        health = agent.health

        assert health["name"] == "Health Test"
        assert "handlers" in health
        assert "plugins" in health
        assert "llm" in health
        # Should not have MCP-specific health info or it should be empty

    def test_health_with_llm_no_mcp(self):
        """Test health shows LLM available but no MCP."""
        llm = MockLLMProvider()
        agent = SubstrateAgent(
            name="Health Test",
            llm_provider=llm,
            api_key="test_key"
        )

        health = agent.health

        assert health["llm"]["available"] is True


class TestCallMCPToolErrorHandling:
    """Test error handling when calling MCP tools without MCP configured."""

    @pytest.mark.asyncio
    async def test_call_mcp_tool_without_mcp_configured(self):
        """Test that calling MCP tool without MCP raises helpful error."""
        agent = SubstrateAgent(name="No MCP Agent")

        with pytest.raises(RuntimeError, match="No MCP servers configured"):
            await agent.call_mcp_tool("some_tool", {"arg": "value"})


class TestStopCleanupWithoutMCP:
    """Test agent cleanup works without MCP."""

    @pytest.mark.asyncio
    async def test_stop_cleanup_no_mcp(self):
        """Test agent stop/cleanup works when no MCP configured."""
        agent = SubstrateAgent(name="Cleanup Test")
        await agent.start()

        # Should not error during cleanup
        await agent.stop()

        # Verify cleanup completed without errors (no assertion needed)


class TestBackwardCompatibility:
    """Test backward compatibility with existing code."""

    @pytest.mark.asyncio
    async def test_existing_agent_code_still_works(self):
        """Test that existing SubstrateAgent usage patterns still work."""
        # Pattern 1: Minimal agent
        agent1 = SubstrateAgent()
        assert agent1 is not None

        # Pattern 2: Agent with name
        agent2 = SubstrateAgent(name="My Agent")
        assert agent2.name == "My Agent"

        # Pattern 3: Agent with LLM
        llm = MockLLMProvider()
        agent3 = SubstrateAgent(llm_provider=llm, api_key="test_key")
        assert agent3.llm is not None

        # Pattern 4: Agent with config
        config = AgentConfig(name="Config Agent")
        agent4 = SubstrateAgent(config=config)
        assert agent4.name == "Config Agent"

        # Pattern 5: Agent with handlers
        async def handler(data, context, agent):
            return {"ok": True}

        agent5 = SubstrateAgent(handlers={"task": handler})
        assert "task" in agent5.handlers

    @pytest.mark.asyncio
    async def test_full_workflow_without_mcp(self):
        """Test complete agent workflow without MCP."""
        llm = MockLLMProvider()
        llm.set_response("default", "Analysis complete")

        agent = SubstrateAgent(
            name="Workflow Test",
            llm_provider=llm,
            focus=["important_field"],
            relay=None,
            api_key="test_key"
        )

        await agent.start()

        # Multiple operations
        r1 = await agent.process("analyze", {"important_field": "data", "other": "ignored"})
        assert r1["status"] == "success"

        r2 = await agent.process("transform", {"key": "value"})
        assert r2["status"] == "success"

        r3 = await agent.process("llm_query", "What is this?")
        assert r3["status"] == "success"

        # Token usage should work
        usage = agent.get_token_usage()
        assert isinstance(usage, dict)

        await agent.stop()


class TestNoMCPDependenciesLoaded:
    """Test that MCP dependencies are not loaded when not used."""

    def test_mcp_module_not_imported(self):
        """Test that MCP plugin module is not imported unless needed."""
        import sys

        # Create agent without MCP
        agent = SubstrateAgent(name="Test")

        # The MCP module might be imported by other tests, so we check
        # that it's not required for basic agent operation
        # This is more of a documentation of the behavior
        assert agent.mcp_registry is None


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