import json

import httpx
import pytest
import respx

from mrp.agent import Agent
from mrp.auth import Keypair
from mrp.models import Message


@pytest.fixture
def keypair():
    return Keypair.generate()


def _mock_register(keypair):
    """Set up mock for agent registration (PATCH)."""
    pk = keypair.public_key_b64
    respx.patch(f"http://localhost:8080/v1/agents/{pk}").mock(
        return_value=httpx.Response(200, json={
            "public_key": pk, "display_name": "Test", "status": "active",
            "metadata": {}, "capabilities": [],
            "created_at": "2026-03-05T12:00:00Z",
            "last_active_at": "2026-03-05T12:00:00Z",
        })
    )


class TestAgentInit:
    def test_generate_keypair(self):
        agent = Agent("http://localhost:8080")
        assert len(agent.public_key) == 43

    def test_from_keypair(self, keypair):
        agent = Agent("http://localhost:8080", keypair=keypair)
        assert agent.public_key == keypair.public_key_b64

    def test_from_key_file(self, tmp_path):
        key_path = str(tmp_path / "agent.key")
        agent = Agent("http://localhost:8080", key_file=key_path)
        assert len(agent.public_key) == 43
        # File should be created
        assert (tmp_path / "agent.key").exists()


class TestAgentRegister:
    @respx.mock
    def test_register(self, keypair):
        agent = Agent("http://localhost:8080", keypair=keypair, name="Test Agent")
        _mock_register(keypair)
        info = agent.register()
        assert info.public_key == keypair.public_key_b64

    @respx.mock
    def test_auto_register_on_send(self, keypair):
        agent = Agent("http://localhost:8080", keypair=keypair)
        _mock_register(keypair)
        respx.post("http://localhost:8080/v1/messages").mock(
            return_value=httpx.Response(202, json={
                "message_id": "msg_1", "status": "sent",
                "created_at": "2026-03-05T12:00:00Z",
                "expires_at": "2026-03-12T12:00:00Z",
            })
        )
        result = agent.send(to="recipient_key", body={"text": "hi"})
        assert result.message_id == "msg_1"


class TestAgentMessaging:
    @respx.mock
    def test_send(self, keypair):
        agent = Agent("http://localhost:8080", keypair=keypair)
        _mock_register(keypair)
        respx.post("http://localhost:8080/v1/messages").mock(
            return_value=httpx.Response(202, json={
                "message_id": "msg_1", "status": "sent",
                "created_at": "2026-03-05T12:00:00Z",
                "expires_at": "2026-03-12T12:00:00Z",
            })
        )
        result = agent.send(to="rkey", body={"hello": "world"})
        assert result.status == "sent"

    @respx.mock
    def test_reply(self, keypair):
        agent = Agent("http://localhost:8080", keypair=keypair)
        _mock_register(keypair)
        respx.post("http://localhost:8080/v1/messages").mock(
            return_value=httpx.Response(202, json={
                "message_id": "msg_2", "status": "sent",
                "created_at": "2026-03-05T12:00:00Z",
                "expires_at": "2026-03-12T12:00:00Z",
            })
        )
        msg = Message(
            message_id="msg_1", sender_key="other_key", recipient_key=keypair.public_key_b64,
            content_type="application/json", body={"text": "hi"},
            attachments=[], status="delivered", thread_id="t1",
            in_reply_to=None, metadata={},
            created_at="2026-03-05T12:00:00Z", expires_at="2026-03-12T12:00:00Z",
        )
        result = agent.reply(msg, {"text": "hello back"})
        assert result.message_id == "msg_2"

    @respx.mock
    def test_receive(self, keypair):
        agent = Agent("http://localhost:8080", keypair=keypair)
        _mock_register(keypair)
        respx.get("http://localhost:8080/v1/messages").mock(
            return_value=httpx.Response(200, json={
                "messages": [
                    {
                        "message_id": "msg_1", "sender_key": "s", "recipient_key": "r",
                        "content_type": "application/json", "body": {},
                        "attachments": [], "status": "delivered",
                        "metadata": {},
                        "created_at": "2026-03-05T12:00:00Z",
                        "expires_at": "2026-03-12T12:00:00Z",
                    }
                ],
                "next_cursor": None,
            })
        )
        msgs = agent.receive()
        assert len(msgs) == 1


class TestAgentDiscovery:
    @respx.mock
    def test_discover(self, keypair):
        agent = Agent("http://localhost:8080", keypair=keypair)
        _mock_register(keypair)
        respx.get("http://localhost:8080/v1/discover").mock(
            return_value=httpx.Response(200, json={
                "agents": [
                    {"public_key": "pk1", "display_name": "A",
                     "last_active_at": "2026-03-05T12:00:00Z",
                     "capabilities": ["chat"]}
                ],
                "next_cursor": None,
            })
        )
        agents = agent.discover("chat")
        assert len(agents) == 1


class TestAgentBlobs:
    @respx.mock
    def test_upload_and_download(self, keypair):
        agent = Agent("http://localhost:8080", keypair=keypair)
        _mock_register(keypair)
        respx.post("http://localhost:8080/v1/blobs").mock(
            return_value=httpx.Response(201, json={
                "blob_id": "blob_1", "hash": "abc", "size": 5,
                "content_type": "text/plain",
                "created_at": "2026-03-05T12:00:00Z",
                "expires_at": "2026-03-12T12:00:00Z",
            })
        )
        blob = agent.upload(b"hello", "text/plain")
        assert blob.blob_id == "blob_1"


class TestAgentLifecycle:
    @respx.mock
    def test_delete(self, keypair):
        agent = Agent("http://localhost:8080", keypair=keypair)
        pk = keypair.public_key_b64
        respx.delete(f"http://localhost:8080/v1/agents/{pk}").mock(
            return_value=httpx.Response(204)
        )
        agent.delete()

    def test_context_manager(self, keypair):
        with Agent("http://localhost:8080", keypair=keypair) as agent:
            assert len(agent.public_key) == 43
