"""Tests for create_opencode_style_deepagent"""

import pytest
from unittest.mock import MagicMock, AsyncMock

from langchain_anthropic import ChatAnthropic
from langchain_core.messages import AIMessage, HumanMessage
from langchain_core.tools import tool

from langchain_agentx.opencode_style_deep_agent import create_opencode_style_deepagent


# ═════════════════════════════════════════════════════════════════════════════
# Fixtures
# ═════════════════════════════════════════════════════════════════════════════


@pytest.fixture
def mock_model():
    """Mock model for testing."""
    model = MagicMock(spec=ChatAnthropic)
    model.invoke = MagicMock(return_value=AIMessage(content="Hello"))
    model.ainvoke = AsyncMock(return_value=AIMessage(content="Hello"))
    return model


@pytest.fixture
def sample_tool():
    """Sample tool for testing."""
    @tool
    def test_tool(input: str) -> str:
        """Test tool."""
        return f"Processed: {input}"
    return test_tool


# ═════════════════════════════════════════════════════════════════════════════
# Basic Functionality Tests
# ═════════════════════════════════════════════════════════════════════════════


def test_create_basic_agent(sample_tool):
    """Test creating a basic agent."""
    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[sample_tool],
    )

    assert agent is not None
    assert hasattr(agent, "invoke")


def test_create_agent_with_model_instance(mock_model, sample_tool):
    """Test creating an agent with a model instance."""
    agent = create_opencode_style_deepagent(
        model=mock_model,
        tools=[sample_tool],
    )

    assert agent is not None


def test_create_agent_with_system_prompt(sample_tool):
    """Test creating an agent with a custom system prompt."""
    system_prompt = "You are a helpful assistant."

    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[sample_tool],
        system_prompt=system_prompt,
    )

    assert agent is not None


def test_create_agent_with_max_steps(sample_tool):
    """Test creating an agent with custom max_steps."""
    max_steps = 30

    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[sample_tool],
        max_steps=max_steps,
    )

    assert agent is not None


def test_create_agent_with_skills(sample_tool):
    """Test creating an agent with skills."""
    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[sample_tool],
        skills=["/skills/test/"],
    )

    assert agent is not None


def test_create_agent_with_memory(sample_tool):
    """Test creating an agent with memory."""
    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[sample_tool],
        memory=["/memory/test.md"],
    )

    assert agent is not None


def test_create_agent_with_subagents(sample_tool):
    """Test creating an agent with subagents."""
    subagents = [
        {
            "name": "test_subagent",
            "description": "Test subagent for testing",
            "prompt": "You are a test subagent.",
        }
    ]

    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[sample_tool],
        subagents=subagents,
    )

    assert agent is not None


def test_create_agent_with_middleware(sample_tool):
    """Test creating an agent with custom middleware."""
    mock_middleware = MagicMock()

    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[sample_tool],
        middleware=[mock_middleware],
    )

    assert agent is not None


# ═════════════════════════════════════════════════════════════════════════════
# Parameter Combination Tests
# ═════════════════════════════════════════════════════════════════════════════


def test_create_agent_with_all_params(sample_tool):
    """Test creating an agent with all parameters."""
    subagents = [
        {
            "name": "test_subagent",
            "description": "Test subagent",
            "prompt": "You are a test subagent.",
        }
    ]

    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[sample_tool],
        system_prompt="You are a helpful assistant.",
        middleware=[],
        subagents=subagents,
        skills=["/skills/test/"],
        memory=["/memory/test.md"],
        max_steps=30,
        debug=True,
        name="TestAgent",
    )

    assert agent is not None


# ═════════════════════════════════════════════════════════════════════════════
# Edge Cases and Error Handling
# ═════════════════════════════════════════════════════════════════════════════


def test_create_agent_without_tools():
    """Test creating an agent without tools."""
    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=None,
    )

    assert agent is not None


def test_create_agent_with_empty_tools():
    """Test creating an agent with empty tools list."""
    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[],
    )

    assert agent is not None


def test_create_agent_without_model():
    """Test creating an agent without specifying model (uses default)."""
    agent = create_opencode_style_deepagent(
        model=None,
    )

    assert agent is not None


# ═════════════════════════════════════════════════════════════════════════════
# Integration Tests (Skipped - require actual LLM calls)
# ═════════════════════════════════════════════════════════════════════════════


@pytest.mark.skip(reason="Requires network access and API key")
def test_agent_invocation_with_real_llm(sample_tool):
    """Test agent invocation with real LLM."""
    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[sample_tool],
    )

    result = agent.invoke(
        {"messages": [HumanMessage(content="Hello")]}
    )

    assert result is not None
    assert "messages" in result


@pytest.mark.skip(reason="Requires full LLM mock setup")
async def test_agent_stream_with_finish_reason(sample_tool):
    """Test agent streaming with finish_reason handling."""
    # This would require mocking the LLM to return specific finish_reason values
    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[sample_tool],
    )

    chunks = []
    async for chunk in agent.astream(
        {"messages": [HumanMessage(content="Hello")]}
    ):
        chunks.append(chunk)

    assert len(chunks) > 0


# ═════════════════════════════════════════════════════════════════════════════
# Comparison with Original deepagents
# ═════════════════════════════════════════════════════════════════════════════


def test_interface_compatibility_with_deepagents(sample_tool):
    """Test that our interface is compatible with deepagents.create_deep_agent."""
    # Our function should accept all the same parameters as deepagents.create_deep_agent
    # This test ensures we haven't broken the interface

    # Import the original function to compare signatures
    try:
        from deepagents import create_deep_agent
        import inspect

        original_sig = inspect.signature(create_deep_agent)
        our_sig = inspect.signature(create_opencode_style_deepagent)

        # Check that our signature includes all original parameters
        # (we may have additional ones like max_steps)
        original_params = set(original_sig.parameters.keys())
        our_params = set(our_sig.parameters.keys())

        # All original params should be in our params
        assert original_params.issubset(our_params)

    except ImportError:
        pytest.skip("deepagents not available for comparison")


# ═════════════════════════════════════════════════════════════════════════════
# Agent Configuration Tests
# ═════════════════════════════════════════════════════════════════════════════


def test_agent_has_correct_default_model():
    """Test that the default model is set correctly."""
    from .graph import get_default_model

    default_model = get_default_model()

    assert default_model is not None
    assert hasattr(default_model, "model_name")
    assert "claude-sonnet-4-5-20250929" in str(default_model.model_name)


def test_base_agent_prompt_is_defined():
    """Test that the base agent prompt is defined."""
    from .graph import BASE_AGENT_PROMPT

    assert BASE_AGENT_PROMPT is not None
    assert isinstance(BASE_AGENT_PROMPT, str)
    assert len(BASE_AGENT_PROMPT) > 0


# ═════════════════════════════════════════════════════════════════════════════
# Subagent Processing Tests
# ═════════════════════════════════════════════════════════════════════════════


def test_subagent_with_custom_model(sample_tool):
    """Test creating a subagent with a custom model."""
    subagents = [
        {
            "name": "test_subagent",
            "description": "Test subagent",
            "prompt": "You are a test subagent.",
            "model": "claude-3-5-sonnet-20241022",
        }
    ]

    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[sample_tool],
        subagents=subagents,
    )

    assert agent is not None


def test_subagent_with_custom_tools(sample_tool):
    """Test creating a subagent with custom tools."""
    @tool
    def subagent_tool(x: int) -> int:
        """Subagent tool."""
        return x * 2

    subagents = [
        {
            "name": "test_subagent",
            "description": "Test subagent",
            "prompt": "You are a test subagent.",
            "tools": [subagent_tool],
        }
    ]

    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[sample_tool],
        subagents=subagents,
    )

    assert agent is not None


def test_subagent_with_custom_middleware(sample_tool):
    """Test creating a subagent with custom middleware."""
    mock_middleware = MagicMock()

    subagents = [
        {
            "name": "test_subagent",
            "description": "Test subagent",
            "prompt": "You are a test subagent.",
            "middleware": [mock_middleware],
        }
    ]

    agent = create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[sample_tool],
        subagents=subagents,
    )

    assert agent is not None


def test_subagent_with_compiled_subagent(sample_tool):
    """Test creating an agent with a compiled subagent."""
    compiled_subagent = {
        "name": "test_subagent",
        "runnable": MagicMock(),  # A pre-compiled runnable
    }

    agent = (create_opencode_style_deepagent(
        model="claude-sonnet-4-5-20250929",
        tools=[sample_tool],
        subagents=[compiled_subagent],
    ))

    assert agent is not None
