from __future__ import annotations

from pathlib import Path

import pytest

from agentrspy.config import (
    load_agent_from_yaml_str,
    load_multi_agent_from_yaml_str,
    load_runtime_from_yaml_str,
)
from agentrspy.core.testing import MockLlmProvider
from agentrspy.prelude import (
    Agent,
    CalculatorTool,
    MultiAgentOrchestrator,
    RoutingStrategy,
)


pytestmark = pytest.mark.anyio


async def test_loads_agent_from_yaml_string() -> None:
    yaml_text = """
name: yaml-agent
llm:
  provider: open_ai
  api_key: test-key
  base_url: http://localhost:1234/v1
  model: test-model
system: You are loaded from YAML.
tools:
  - type: calculator
loop_strategy:
  type: chain_of_thought
"""

    result = await load_agent_from_yaml_str(yaml_text)
    assert result is not None


async def test_loads_multi_agent_from_yaml_string() -> None:
    yaml_text = """
agents:
  - name: first
    llm:
      provider: open_ai
      api_key: test-key
      base_url: http://localhost:1234/v1
      model: test-model
    system: First agent.
  - name: second
    llm:
      provider: open_ai
      api_key: test-key
      base_url: http://localhost:1234/v1
      model: test-model
    system: Second agent.
routing:
  type: parallel
  agents:
    - first
    - second
"""

    result = await load_multi_agent_from_yaml_str(yaml_text)
    assert result is not None


async def test_loads_runtime_from_yaml_string() -> None:
    yaml_text = """
kind: agent
llm:
  provider: open_ai
  api_key: test-key
  base_url: http://localhost:1234/v1
  model: test-model
tools:
  - type: calculator
"""

    result = await load_runtime_from_yaml_str(yaml_text)
    assert result is not None


async def test_agent_executes_tool_calls() -> None:
    llm = MockLlmProvider.with_tool_call_sequence(
        "calculator",
        {"expression": "2 + 2"},
        "The answer is 4.",
    )

    agent = Agent.builder().llm(llm).tool(CalculatorTool()).build()
    output = await agent.run("What is 2 + 2?")

    assert "4" in output.text
    assert output.steps >= 1


async def test_sequential_multi_agent_pipeline() -> None:
    researcher = (
        Agent.builder()
        .llm(
            MockLlmProvider.with_text_responses(
                ["Rust uses ownership to prevent data races."]
            )
        )
        .build()
    )
    writer = (
        Agent.builder()
        .llm(
            MockLlmProvider.with_text_responses(
                ["Final answer: ownership enables safe concurrency."]
            )
        )
        .build()
    )

    orchestrator = (
        MultiAgentOrchestrator.builder()
        .add_agent("researcher", researcher)
        .add_agent("writer", writer)
        .routing(RoutingStrategy.sequential(["researcher", "writer"]))
        .build()
    )

    output = await orchestrator.run("Explain Rust concurrency")
    assert "Final answer" in output.text


async def test_python_module_entrypoint_exists() -> None:
    assert Path("agentrspy/__main__.py").exists()
