from __future__ import annotations

from datetime import datetime, timezone
from unittest.mock import MagicMock

import pytest

from mcp.server.fastmcp import FastMCP
from mrp.models import DiscoveredAgent, Message, PollResult, SendResult

from mrp_mcp.tools.messaging import register


def _now():
    return datetime.now(timezone.utc)


@pytest.fixture
def mcp_server(mock_agent, state):
    mcp = FastMCP("test")
    register(mcp, mock_agent, state)
    return mcp


@pytest.fixture
def state_with_contact(state):
    state.add_contact(
        DiscoveredAgent(
            public_key="agent-pk-abcdefghijklmnopqrstuvwxyz12345",
            display_name="TranslatorBot",
            last_active_at=_now(),
            capabilities=["text:translate"],
        )
    )
    return state


@pytest.mark.asyncio
async def test_send_by_name(mcp_server, mock_agent, state_with_contact):
    result = await mcp_server.call_tool(
        "mrp_send", {"to": "TranslatorBot", "message": "Hello!"}
    )
    text = result[0][0].text

    mock_agent.send.assert_called_once()
    call_kwargs = mock_agent.send.call_args
    assert call_kwargs.kwargs["to"] == "agent-pk-abcdefghijklmnopqrstuvwxyz12345"
    assert call_kwargs.kwargs["body"] == {"text": "Hello!"}
    assert "Message sent to **TranslatorBot**" in text
    assert "msg_002" in text


@pytest.mark.asyncio
async def test_send_with_thread(mcp_server, mock_agent, state_with_contact):
    result = await mcp_server.call_tool(
        "mrp_send",
        {"to": "TranslatorBot", "message": "Hi", "thread_id": "thread-1"},
    )
    text = result[0][0].text

    call_kwargs = mock_agent.send.call_args
    assert call_kwargs.kwargs["thread"] == "thread-1"
    assert "thread-1" in text


@pytest.mark.asyncio
async def test_send_unknown_recipient(mcp_server):
    with pytest.raises(Exception, match="Unknown recipient"):
        await mcp_server.call_tool(
            "mrp_send", {"to": "nobody", "message": "Hello!"}
        )


@pytest.mark.asyncio
async def test_reply(mcp_server, mock_agent, state_with_contact):
    # Set up the original message's sender to be in contacts
    state_with_contact.add_contact(
        DiscoveredAgent(
            public_key="sender-pk-abcdefghijklmnopqrstuvwxyz1234",
            display_name="SenderBot",
            last_active_at=_now(),
            capabilities=[],
        )
    )

    result = await mcp_server.call_tool(
        "mrp_reply", {"message_id": "msg_001", "message": "Got it!"}
    )
    text = result[0][0].text

    mock_agent.client.get_message.assert_called_once_with("msg_001")
    mock_agent.reply.assert_called_once()
    assert "Reply sent to **SenderBot**" in text
    assert "msg_003" in text


@pytest.mark.asyncio
async def test_check_messages_empty(mcp_server, mock_agent):
    result = await mcp_server.call_tool("mrp_check_messages", {})
    text = result[0][0].text
    assert "No new messages" in text


@pytest.mark.asyncio
async def test_check_messages_with_results(mcp_server, mock_agent, state, sample_messages):
    mock_agent.client.poll_messages.return_value = PollResult(
        messages=sample_messages, next_cursor=None
    )
    # Mock get_agent for unknown senders
    mock_agent.client.get_agent.return_value = MagicMock(
        public_key="sender-pk-abcdefghijklmnopqrstuvwxyz1234",
        display_name="SenderBot",
        last_active_at=_now(),
        capabilities=[],
    )

    result = await mcp_server.call_tool("mrp_check_messages", {})
    text = result[0][0].text

    assert "2 new message(s)" in text
    assert "Hello there!" in text
    assert "How are you?" in text


@pytest.mark.asyncio
async def test_check_messages_from_unread_queue(mcp_server, mock_agent, state, sample_messages):
    # Add messages to unread queue (simulating background poller)
    state.unread.extend(sample_messages)
    mock_agent.client.get_agent.return_value = MagicMock(
        public_key="sender-pk-abcdefghijklmnopqrstuvwxyz1234",
        display_name="SenderBot",
        last_active_at=_now(),
        capabilities=[],
    )

    result = await mcp_server.call_tool("mrp_check_messages", {"limit": 1})
    text = result[0][0].text

    assert "1 new message(s)" in text
    # One message should remain in unread
    assert len(state.unread) == 1
