"""Tests for A2A sender identification feature."""

from unittest.mock import MagicMock, patch

# ============================================================
# CLI Tool: build_sender_info Tests
# ============================================================


class TestBuildSenderInfo:
    """Test build_sender_info function in a2a.py CLI tool."""

    def test_auto_detect_from_pid_matching(self, monkeypatch):
        """Should auto-detect sender info via Registry PID matching."""
        from synapse.tools.a2a import build_sender_info

        monkeypatch.delenv("SYNAPSE_AGENT_ID", raising=False)
        monkeypatch.delenv("SYNAPSE_AGENT_TYPE", raising=False)
        monkeypatch.delenv("SYNAPSE_PORT", raising=False)

        mock_registry = MagicMock()
        mock_registry.list_agents.return_value = {
            "synapse-claude-8100": {
                "agent_id": "synapse-claude-8100",
                "agent_type": "claude",
                "endpoint": "http://localhost:8100",
                "pid": 12345,
            }
        }

        # Mock is_descendant_of to return True for the matching agent
        with (
            patch("synapse.tools.a2a.AgentRegistry", return_value=mock_registry),
            patch("synapse.tools.a2a.is_descendant_of", return_value=True),
        ):
            sender = build_sender_info()

            assert sender["sender_id"] == "synapse-claude-8100"
            assert sender["sender_type"] == "claude"
            assert sender["sender_endpoint"] == "http://localhost:8100"

    def test_explicit_sender_invalid_format_returns_error(self):
        """--from flag with invalid format should return error."""
        from synapse.tools.a2a import build_sender_info

        sender = build_sender_info(explicit_sender="external-agent")

        # Invalid format returns error string
        assert isinstance(sender, str)
        assert "Error" in sender
        assert "synapse-<type>-<port>" in sender

    def test_explicit_sender_type_returns_error(self):
        """--from flag with agent type should return error."""
        from synapse.tools.a2a import build_sender_info

        mock_registry = MagicMock()
        mock_registry.list_agents.return_value = {
            "synapse-claude-8100": {
                "agent_id": "synapse-claude-8100",
                "agent_type": "claude",
                "endpoint": "http://localhost:8100",
                "pid": 12345,
            },
        }

        with patch("synapse.tools.a2a.AgentRegistry", return_value=mock_registry):
            sender = build_sender_info(explicit_sender="claude")

            assert isinstance(sender, str)
            assert "agent type" in sender.lower()
            assert "synapse-claude-8100" in sender

    def test_empty_when_no_matching_agent(self, monkeypatch):
        """Should return empty dict when no agent matches via PID."""
        from synapse.tools.a2a import build_sender_info

        monkeypatch.delenv("SYNAPSE_AGENT_ID", raising=False)

        mock_registry = MagicMock()
        mock_registry.list_agents.return_value = {
            "synapse-claude-8100": {
                "agent_id": "synapse-claude-8100",
                "agent_type": "claude",
                "endpoint": "http://localhost:8100",
                "pid": 12345,
            }
        }

        # Mock is_descendant_of to return False (no match)
        with (
            patch("synapse.tools.a2a.AgentRegistry", return_value=mock_registry),
            patch("synapse.tools.a2a.is_descendant_of", return_value=False),
        ):
            sender = build_sender_info()
            assert sender == {}

    def test_explicit_sender_valid_id_without_registry(self):
        """--from flag with valid ID format works even without registry."""
        from synapse.tools.a2a import build_sender_info

        sender = build_sender_info(explicit_sender="synapse-manual-9999")
        # Valid format but not in registry - still returns dict with sender_id
        assert isinstance(sender, dict)
        assert sender["sender_id"] == "synapse-manual-9999"

    def test_pid_matching_finds_correct_agent(self, monkeypatch):
        """PID matching should find the correct agent among multiple."""
        from synapse.tools.a2a import build_sender_info

        monkeypatch.delenv("SYNAPSE_AGENT_ID", raising=False)

        mock_registry = MagicMock()
        mock_registry.list_agents.return_value = {
            "synapse-claude-8100": {
                "agent_id": "synapse-claude-8100",
                "agent_type": "claude",
                "endpoint": "http://localhost:8100",
                "pid": 12345,
            },
            "synapse-gemini-8110": {
                "agent_id": "synapse-gemini-8110",
                "agent_type": "gemini",
                "endpoint": "http://localhost:8110",
                "pid": 12346,
            },
        }

        # Mock is_descendant_of to return True only for gemini's PID
        def mock_is_descendant(child, ancestor):
            return ancestor == 12346  # Only match gemini

        with (
            patch("synapse.tools.a2a.AgentRegistry", return_value=mock_registry),
            patch("synapse.tools.a2a.is_descendant_of", side_effect=mock_is_descendant),
        ):
            sender = build_sender_info()

            assert sender["sender_id"] == "synapse-gemini-8110"
            assert sender["sender_type"] == "gemini"


# ============================================================
# A2A Client: sender_info Parameter Tests
# ============================================================


class TestA2AClientSenderInfo:
    """Test A2AClient.send_to_local sender_info handling."""

    def test_send_to_local_includes_sender_in_payload(self):
        """send_to_local should include sender info in request payload."""
        import requests

        from synapse.a2a_client import A2AClient

        with patch.object(requests, "post") as mock_post:
            mock_response = MagicMock()
            mock_response.json.return_value = {
                "task": {"id": "task-123", "status": "working"}
            }
            mock_response.raise_for_status = MagicMock()
            mock_post.return_value = mock_response

            client = A2AClient()
            sender_info = {
                "sender_id": "synapse-claude-8100",
                "sender_type": "claude",
                "sender_endpoint": "http://localhost:8100",
            }

            client.send_to_local(
                endpoint="http://localhost:8110",
                message="Hello!",
                sender_info=sender_info,
            )

            # Verify the request payload contains sender metadata
            call_args = mock_post.call_args
            payload = call_args.kwargs.get("json") or call_args[1].get("json")

            assert "metadata" in payload
            assert "sender" in payload["metadata"]
            assert payload["metadata"]["sender"]["sender_id"] == "synapse-claude-8100"

    def test_send_to_local_no_sender_includes_response_mode(self):
        """send_to_local should include response_mode even without sender_info."""
        import requests

        from synapse.a2a_client import A2AClient

        with patch.object(requests, "post") as mock_post:
            mock_response = MagicMock()
            mock_response.json.return_value = {
                "task": {"id": "task-123", "status": "working"}
            }
            mock_response.raise_for_status = MagicMock()
            mock_post.return_value = mock_response

            client = A2AClient()

            client.send_to_local(
                endpoint="http://localhost:8110",
                message="Hello!",
                # No sender_info
            )

            # Verify metadata includes response_mode but no sender
            call_args = mock_post.call_args
            payload = call_args.kwargs.get("json") or call_args[1].get("json")

            assert "metadata" in payload
            assert payload["metadata"]["response_mode"] == "notify"
            assert "sender" not in payload["metadata"]
