"""Tests for AskHumanTool."""

import asyncio
import pytest

from agentkit.tools.ask_human import AskHumanTool


class TestAskHumanToolBasic:
    def test_tool_properties(self):
        tool = AskHumanTool()
        assert tool.name == "ask_human"
        assert "question" in str(tool.parameters)
        assert tool.parameters["required"] == ["question"]

    @pytest.mark.asyncio
    async def test_no_chat_mode_returns_default(self):
        tool = AskHumanTool()
        result = await tool.execute(question="What should I do?")
        assert result == {"reply": "confirmed"}

    @pytest.mark.asyncio
    async def test_no_chat_mode_with_options(self):
        tool = AskHumanTool()
        result = await tool.execute(question="Choose:", options=["A", "B", "C"])
        assert result == {"reply": "A"}


class TestAskHumanToolChatMode:
    @pytest.mark.asyncio
    async def test_ask_and_receive_reply(self):
        tool = AskHumanTool(timeout=5.0)
        pending: dict[str, asyncio.Future] = {}
        ask_calls: list[tuple[str, str, list[str] | None]] = []

        async def mock_ask_callback(request_id, question, options):
            ask_calls.append((request_id, question, options))

        tool.configure(pending_replies=pending, ask_callback=mock_ask_callback)

        # Start the execute in a task
        task = asyncio.create_task(
            tool.execute(question="Continue?", options=["yes", "no"])
        )

        # Wait for the ask to be pushed
        await asyncio.sleep(0.1)
        assert len(ask_calls) == 1
        request_id = ask_calls[0][0]
        assert ask_calls[0][1] == "Continue?"
        assert ask_calls[0][2] == ["yes", "no"]

        # Simulate user reply
        assert request_id in pending
        pending[request_id].set_result("yes")

        result = await task
        assert result == {"reply": "yes"}

    @pytest.mark.asyncio
    async def test_timeout_returns_default(self):
        tool = AskHumanTool(timeout=0.1)
        pending: dict[str, asyncio.Future] = {}

        async def mock_ask_callback(request_id, question, options):
            pass  # Never reply

        tool.configure(pending_replies=pending, ask_callback=mock_ask_callback)

        result = await tool.execute(question="Continue?", options=["yes", "no"])
        assert result == {"reply": "yes"}

    @pytest.mark.asyncio
    async def test_timeout_no_options(self):
        tool = AskHumanTool(timeout=0.1)
        pending: dict[str, asyncio.Future] = {}

        async def mock_ask_callback(request_id, question, options):
            pass

        tool.configure(pending_replies=pending, ask_callback=mock_ask_callback)

        result = await tool.execute(question="Continue?")
        assert "timeout" in result["reply"]

    @pytest.mark.asyncio
    async def test_cleanup_on_reply(self):
        tool = AskHumanTool(timeout=5.0)
        pending: dict[str, asyncio.Future] = {}

        async def mock_ask_callback(request_id, question, options):
            # Immediately reply
            await asyncio.sleep(0.05)
            pending[request_id].set_result("ok")

        tool.configure(pending_replies=pending, ask_callback=mock_ask_callback)

        await tool.execute(question="Test?")
        # Pending should be cleaned up
        assert len(pending) == 0
