"""Integration tests for one_call_at_a_time tool concurrency."""

import pytest
from pydantic import BaseModel, Field

from agency_swarm import Agent, BaseTool


class TestToolConcurrencyEndToEnd:
    """End-to-end integration test for one_call_at_a_time functionality."""

    class ToolExecutionReport(BaseModel):
        """Output type that captures any tool execution errors."""

        sequential_tool_result: str = Field(description="Result from the sequential tool")
        parallel_tool_result: str = Field(description="Result from the parallel tool")
        errors_encountered: list[str] = Field(
            default_factory=list,
            description="List of any errors or concurrency violations that occurred "
            "containing exact error messages that you've received",
        )

    @pytest.mark.asyncio
    async def test_agent_enforces_tool_concurrency(self):
        """Test that agent properly enforces one_call_at_a_time using structured output."""

        class SequentialTool(BaseTool):
            """A tool that must run sequentially and takes time."""

            duration: float = Field(description="How long to process in seconds")

            class ToolConfig:
                one_call_at_a_time = True
                strict = False

            def run(self):
                import time

                time.sleep(self.duration)
                return f"SequentialTool completed processing for {self.duration} seconds"

        class ParallelTool(BaseTool):
            """A tool that can run in parallel."""

            message: str = Field(description="Message to process")

            class ToolConfig:
                strict = False

            def run(self):
                return f"ParallelTool processed: {self.message}"

        # Create agent with structured output for response validation
        agent = Agent(
            name="ConcurrencyTestAgent",
            instructions="""You are a test agent with two tools: SequentialTool and ParallelTool.

            When asked to use both tools simultaneously:
            1. Try to call SequentialTool with duration=2
            2. Try to call ParallelTool with message="test_parallel"
            3. Report the results and any errors that occur

            IMPORTANT: Always call both tools in a single response, not sequentially.
            If you encounter tool concurrency violations, include them in the errors_encountered list.""",
            tools=[SequentialTool, ParallelTool],
            output_type=self.ToolExecutionReport,
            model="gpt-5.4-mini",
        )

        # Ask agent to use both tools simultaneously
        response = await agent.get_response(
            "Please use both SequentialTool and ParallelTool at the same time. "
            "Call SequentialTool with duration 1 and ParallelTool with message 'test_parallel'. "
            "Report any concurrency violations or errors in the structured output."
        )

        # Verify the structured output
        output = response.final_output
        assert isinstance(output, self.ToolExecutionReport)

        # Check tool outputs directly for concurrency violations; avoid relying on summary wording
        tool_outputs = [str(item.output) for item in response.new_items if hasattr(item, "output")]
        concurrency_errors = [out for out in tool_outputs if "concurrency violation" in out.lower()]
        assert len(concurrency_errors) > 0, f"Expected concurrency violation, but got tool outputs: {tool_outputs}"

        # At least one tool should have completed successfully
        success_markers = (
            f"{SequentialTool.__name__} completed processing".lower(),
            f"{ParallelTool.__name__} processed".lower(),
        )
        successful_results = [
            output
            for output in tool_outputs
            if any(marker in output.lower() for marker in success_markers) and "error" not in output.lower()
        ]
        assert len(successful_results) > 0, "At least one tool should have completed successfully"


class TestFunctionToolConcurrency:
    """Test concurrency with @function_tool decorated tools."""

    def test_function_tool_tools_folder_integration(self, tmp_path):
        """Test that function tools from tools_folder get proper concurrency handling."""

        # Create a tools folder with function tools
        tools_dir = tmp_path / "tools"
        tools_dir.mkdir()

        # Create function tool file
        tool_file = tools_dir / "concurrency_tool.py"
        tool_file.write_text("""
from agents import function_tool
import time

@function_tool
def sequential_tool(duration: float) -> str:
    '''A tool that must run sequentially.'''
    time.sleep(duration)
    return f"Sequential tool completed after {duration}s"

# Set one_call_at_a_time attribute
sequential_tool.one_call_at_a_time = True

@function_tool
def parallel_tool(message: str) -> str:
    '''A tool that can run in parallel.'''
    return f"Parallel tool: {message}"
""")

        # Create agent with tools_folder
        agent = Agent(
            name="ToolsFolderAgent",
            instructions="Test tools folder integration.",
            tools_folder=str(tools_dir),
            model="gpt-5.4-mini",
        )

        # Should have loaded both tools
        tool_names = [tool.name for tool in agent.tools]
        assert "sequential_tool" in tool_names
        assert "parallel_tool" in tool_names

        # Find the tools
        sequential_tool = next(t for t in agent.tools if t.name == "sequential_tool")
        parallel_tool = next(t for t in agent.tools if t.name == "parallel_tool")

        # Sequential tool should have one_call_at_a_time
        assert getattr(sequential_tool, "one_call_at_a_time", False) is True
        assert getattr(sequential_tool, "_one_call_guard_installed", False) is True

        # Parallel tool should not
        assert getattr(parallel_tool, "one_call_at_a_time", False) is False
        assert getattr(parallel_tool, "_one_call_guard_installed", False) is True
