"""Tests for b2alpha.api – HTTP client (mocked, no real network calls)."""

from __future__ import annotations

from unittest.mock import patch, MagicMock

import pytest


def _mock_response(status: int, body: dict) -> MagicMock:
    resp = MagicMock()
    resp.status_code = status
    resp.json.return_value = body
    return resp


@pytest.fixture(autouse=True)
def _env_config(monkeypatch):
    monkeypatch.setenv("B2A_SUPABASE_URL", "https://test.supabase.co")
    monkeypatch.setenv("B2A_SUPABASE_ANON_KEY", "test-anon-key")


# ---------------------------------------------------------------------------
# register_agent
# ---------------------------------------------------------------------------

class TestRegisterAgent:
    @patch("b2alpha.api.httpx.post")
    def test_success(self, mock_post):
        mock_post.return_value = _mock_response(201, {
            "ok": True,
            "agent": {"did": "did:b2a:zABC", "display_name": "Bot"},
        })

        from b2alpha.api import register_agent
        result = register_agent("tok", {"did": "did:b2a:zABC", "public_key": "pk"})

        assert result["ok"] is True
        assert result["agent"]["did"] == "did:b2a:zABC"
        mock_post.assert_called_once()
        call_kwargs = mock_post.call_args
        assert "Bearer tok" in call_kwargs.kwargs["headers"]["Authorization"]

    @patch("b2alpha.api.httpx.post")
    def test_error_raises(self, mock_post):
        mock_post.return_value = _mock_response(409, {"error": "did_already_registered"})

        from b2alpha.api import register_agent
        with pytest.raises(RuntimeError, match="did_already_registered"):
            register_agent("tok", {"did": "did:b2a:zABC", "public_key": "pk"})


# ---------------------------------------------------------------------------
# update_agent
# ---------------------------------------------------------------------------

class TestUpdateAgent:
    @patch("b2alpha.api.httpx.patch")
    def test_success(self, mock_patch):
        mock_patch.return_value = _mock_response(200, {"ok": True, "did": "did:b2a:zABC"})

        from b2alpha.api import update_agent
        result = update_agent("tok", {"did": "did:b2a:zABC", "display_name": "New"})
        assert result["ok"] is True

    @patch("b2alpha.api.httpx.patch")
    def test_not_owner(self, mock_patch):
        mock_patch.return_value = _mock_response(403, {"error": "not_owner"})

        from b2alpha.api import update_agent
        with pytest.raises(RuntimeError, match="not_owner"):
            update_agent("tok", {"did": "did:b2a:zABC"})


# ---------------------------------------------------------------------------
# lookup_agent
# ---------------------------------------------------------------------------

class TestLookupAgent:
    @patch("b2alpha.api.httpx.get")
    def test_found(self, mock_get):
        mock_get.return_value = _mock_response(200, {
            "agent": {"did": "did:b2a:zABC", "display_name": "Bot", "agent_type": 0},
        })

        from b2alpha.api import lookup_agent
        result = lookup_agent("did:b2a:zABC")
        assert result["agent"]["did"] == "did:b2a:zABC"

        # Should use anon headers (no Bearer token)
        call_kwargs = mock_get.call_args
        assert "Authorization" not in call_kwargs.kwargs["headers"]

    @patch("b2alpha.api.httpx.get")
    def test_not_found(self, mock_get):
        mock_get.return_value = _mock_response(404, {"error": "agent_not_found"})

        from b2alpha.api import lookup_agent
        with pytest.raises(RuntimeError, match="agent_not_found"):
            lookup_agent("did:b2a:zNOPE")


# ---------------------------------------------------------------------------
# search_phonebook
# ---------------------------------------------------------------------------

class TestSearchPhonebook:
    @patch("b2alpha.api.httpx.get")
    def test_with_query(self, mock_get):
        mock_get.return_value = _mock_response(200, {"agents": [{"did": "did:b2a:z1"}], "count": 1})

        from b2alpha.api import search_phonebook
        result = search_phonebook(query="weather", agent_type="business", limit=5)
        assert result["count"] == 1

        call_kwargs = mock_get.call_args
        assert call_kwargs.kwargs["params"]["q"] == "weather"
        assert call_kwargs.kwargs["params"]["type"] == "business"
        assert call_kwargs.kwargs["params"]["limit"] == "5"

    @patch("b2alpha.api.httpx.get")
    def test_empty_results(self, mock_get):
        mock_get.return_value = _mock_response(200, {"agents": [], "count": 0})

        from b2alpha.api import search_phonebook
        result = search_phonebook()
        assert result["agents"] == []


# ---------------------------------------------------------------------------
# list_conversations
# ---------------------------------------------------------------------------

class TestListConversations:
    @patch("b2alpha.api.httpx.get")
    def test_success(self, mock_get):
        mock_get.return_value = _mock_response(200, {"conversations": [
            {"interaction_id": "conv1", "message_count": 3},
        ]})

        from b2alpha.api import list_conversations
        result = list_conversations("tok", limit=10, with_did="did:b2a:zPartner")
        assert len(result["conversations"]) == 1

        call_kwargs = mock_get.call_args
        assert call_kwargs.kwargs["params"]["with"] == "did:b2a:zPartner"

    @patch("b2alpha.api.httpx.get")
    def test_unauthorized(self, mock_get):
        mock_get.return_value = _mock_response(401, {"error": "invalid_auth_token"})

        from b2alpha.api import list_conversations
        with pytest.raises(RuntimeError, match="invalid_auth_token"):
            list_conversations("bad-tok")


# ---------------------------------------------------------------------------
# send_message
# ---------------------------------------------------------------------------

class TestSendMessage:
    @patch("b2alpha.api.httpx.post")
    def test_success(self, mock_post):
        mock_post.return_value = _mock_response(200, {
            "ok": True, "interaction_id": "conv1", "message_id": "msg1",
        })

        from b2alpha.api import send_message
        msg = {"message_id": "msg1", "payload": "abc", "signature": "sig"}
        result = send_message("tok", "did:b2a:zA", "did:b2a:zB", msg, interaction_id="conv1")
        assert result["ok"] is True

        body = mock_post.call_args.kwargs["json"]
        assert body["sender_did"] == "did:b2a:zA"
        assert body["interaction_id"] == "conv1"

    @patch("b2alpha.api.httpx.post")
    def test_no_interaction_id(self, mock_post):
        mock_post.return_value = _mock_response(200, {"ok": True, "interaction_id": "auto"})

        from b2alpha.api import send_message
        msg = {"message_id": "m1", "payload": "x", "signature": "s"}
        send_message("tok", "did:b2a:zA", "did:b2a:zB", msg)

        body = mock_post.call_args.kwargs["json"]
        assert "interaction_id" not in body

    @patch("b2alpha.api.httpx.post")
    def test_close_interaction(self, mock_post):
        mock_post.return_value = _mock_response(200, {"ok": True, "interaction_id": "conv1"})

        from b2alpha.api import send_message
        msg = {"message_id": "m1", "payload": "x", "signature": "s"}
        send_message("tok", "did:b2a:zA", "did:b2a:zB", msg, close_interaction=True)

        body = mock_post.call_args.kwargs["json"]
        assert body["close_interaction"] is True


# ---------------------------------------------------------------------------
# list_interaction_records
# ---------------------------------------------------------------------------

class TestListInteractionRecords:
    @patch("b2alpha.api.httpx.get")
    def test_success(self, mock_get):
        mock_get.return_value = _mock_response(200, [
            {"interaction_id": "conv1", "messages": []},
        ])

        from b2alpha.api import list_interaction_records
        result = list_interaction_records("tok", "did:b2a:zMe", limit=25)

        assert result[0]["interaction_id"] == "conv1"
        params = mock_get.call_args.kwargs["params"]
        assert params["limit"] == "25"
        assert "participant_a_did.eq.did:b2a:zMe" in params["or"]

    @patch("b2alpha.api.httpx.get")
    def test_error_raises(self, mock_get):
        mock_get.return_value = _mock_response(403, {"error": "forbidden"})

        from b2alpha.api import list_interaction_records
        with pytest.raises(RuntimeError, match="forbidden"):
            list_interaction_records("tok", "did:b2a:zMe")
