"""
Tests verifying connection_id and cluster_id have been removed from the SDK.

The Atomic Connection Identity design (Phase 3) removes these client-side parameters.
Connection identity is now resolved server-side from the OAuth token's JWT claims.

These tests ensure:
1. BurrowGuard constructor does NOT accept connection_id or cluster_id
2. Scan payload does NOT include connection_id or cluster_id
3. The constructor only accepts the documented parameters
"""

from __future__ import annotations

import inspect
from unittest.mock import MagicMock, patch

import pytest

from burrow import BurrowGuard


class TestConnectionIdRemoved:
    """Verify connection_id parameter has been completely removed."""

    def test_constructor_rejects_connection_id(self):
        """BurrowGuard(**{'connection_id': 'x'}) should raise TypeError."""
        with pytest.raises(TypeError, match="unexpected keyword argument"):
            BurrowGuard(connection_id="some-connection-id")

    def test_constructor_rejects_cluster_id(self):
        """BurrowGuard(**{'cluster_id': 'x'}) should raise TypeError."""
        with pytest.raises(TypeError, match="unexpected keyword argument"):
            BurrowGuard(cluster_id="some-cluster-id")

    def test_constructor_rejects_both(self):
        """BurrowGuard with both connection_id and cluster_id should raise TypeError."""
        with pytest.raises(TypeError):
            BurrowGuard(
                connection_id="conn-123",
                cluster_id="cluster-456",
            )

    def test_no_connection_id_attribute(self):
        """Guard instance should not have a connection_id attribute."""
        guard = BurrowGuard()
        assert not hasattr(guard, "connection_id")

    def test_no_cluster_id_attribute(self):
        """Guard instance should not have a cluster_id attribute."""
        guard = BurrowGuard()
        assert not hasattr(guard, "cluster_id")


class TestConstructorAcceptedParams:
    """Verify the constructor accepts only the documented parameters."""

    EXPECTED_PARAMS = {
        "self",
        "client_id",
        "client_secret",
        "api_url",
        "auth_url",
        "timeout",
        "fail_open",
        "session_id",
    }

    def test_constructor_signature(self):
        """BurrowGuard.__init__ should only accept the expected parameters."""
        sig = inspect.signature(BurrowGuard.__init__)
        actual_params = set(sig.parameters.keys())
        assert actual_params == self.EXPECTED_PARAMS, (
            f"Constructor params changed. Expected {self.EXPECTED_PARAMS}, got {actual_params}"
        )

    def test_constructor_no_connection_in_signature(self):
        """connection_id and cluster_id must NOT appear in the signature."""
        sig = inspect.signature(BurrowGuard.__init__)
        param_names = set(sig.parameters.keys())
        assert "connection_id" not in param_names
        assert "cluster_id" not in param_names


class TestScanPayloadNoConnection:
    """Verify scan payloads don't include connection_id or cluster_id."""

    @patch("burrow.httpx.Client")
    def test_scan_payload_excludes_connection_id(self, mock_client_cls):
        """The POST body to /v1/scan should not contain connection_id."""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "action": "allow",
            "confidence": 0.98,
            "category": "benign",
            "request_id": "req-123",
            "latency_ms": 10.0,
        }

        mock_instance = MagicMock()
        mock_instance.post.return_value = mock_response
        mock_client_cls.return_value = mock_instance

        guard = BurrowGuard(
            client_id="test-client",
            client_secret="test-secret",
            api_url="http://localhost:8001",
        )
        # Bypass OAuth token acquisition
        guard._access_token = "fake-token"
        guard._token_expires_at = 9999999999.0

        guard.scan("Hello world", agent="test-agent")

        # Inspect the POST call
        assert mock_instance.post.called
        call_args = mock_instance.post.call_args
        # The body is either json= kwarg or positional
        body = call_args.kwargs.get("json") or call_args[1].get("json")

        if body:
            assert "connection_id" not in body, "connection_id should not be in scan payload"
            assert "cluster_id" not in body, "cluster_id should not be in scan payload"
            # Also check nested context
            context = body.get("context", {})
            assert "connection_id" not in context, "connection_id should not be in context"
            assert "cluster_id" not in context, "cluster_id should not be in context"

    @patch("burrow.httpx.Client")
    def test_scan_payload_structure(self, mock_client_cls):
        """Verify the scan payload has the expected structure without connection fields."""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "action": "allow",
            "confidence": 0.98,
            "category": "benign",
            "request_id": "req-456",
            "latency_ms": 5.0,
        }

        mock_instance = MagicMock()
        mock_instance.post.return_value = mock_response
        mock_client_cls.return_value = mock_instance

        guard = BurrowGuard(
            client_id="test-client",
            client_secret="test-secret",
            api_url="http://localhost:8001",
        )
        guard._access_token = "fake-token"
        guard._token_expires_at = 9999999999.0

        guard.scan(
            "What is 2+2?",
            agent="test-agent",
            tool_name="calculator",
            content_type="tool_call",
        )

        call_args = mock_instance.post.call_args
        body = call_args.kwargs.get("json") or call_args[1].get("json")

        if body:
            # text must be present
            assert "text" in body
            assert body["text"] == "What is 2+2?"

            # Flatten all keys from body and context
            all_keys = set(body.keys())
            context = body.get("context", {})
            all_keys.update(context.keys())

            # These MUST NOT exist anywhere in the payload
            forbidden = {"connection_id", "cluster_id"}
            found = all_keys & forbidden
            assert not found, f"Forbidden keys found in payload: {found}"


class TestEnvVarsRemoved:
    """Verify BURROW_CONNECTION_ID and BURROW_CLUSTER_ID env vars are not read."""

    @patch.dict(
        "os.environ",
        {
            "BURROW_CONNECTION_ID": "env-conn-123",
            "BURROW_CLUSTER_ID": "env-cluster-456",
        },
    )
    def test_env_vars_not_used(self):
        """Setting BURROW_CONNECTION_ID/BURROW_CLUSTER_ID env vars should have no effect."""
        guard = BurrowGuard()
        assert not hasattr(guard, "connection_id")
        assert not hasattr(guard, "cluster_id")
