"""E2E: Authentication enforcement.

Tests that protected endpoints require valid auth, and public endpoints work
without auth.

Uses the `client` fixture (which has the correct auth header set), and
helpers to make unauthenticated requests by temporarily removing the header.
"""




def _unauth_get(client, url):
    """Make a GET request without the Authorization header."""
    saved = client.headers.pop("Authorization", None)
    try:
        return client.get(url)
    finally:
        if saved is not None:
            client.headers["Authorization"] = saved


def _unauth_request(client, method, url, **kwargs):
    """Make a request without the Authorization header."""
    saved = client.headers.pop("Authorization", None)
    try:
        return client.request(method, url, **kwargs)
    finally:
        if saved is not None:
            client.headers["Authorization"] = saved


def _with_header(client, method, url, auth_value, **kwargs):
    """Make a request with a custom Authorization header."""
    saved = client.headers.get("Authorization")
    client.headers["Authorization"] = auth_value
    try:
        return client.request(method, url, **kwargs)
    finally:
        if saved is not None:
            client.headers["Authorization"] = saved
        else:
            client.headers.pop("Authorization", None)


class TestAuthEnforcement:
    """Verify API key authentication is enforced correctly."""

    def test_request_without_auth_returns_401(self, client):
        """Protected endpoint without Authorization header returns 401."""
        resp = _unauth_get(client, "/api/v1/devices")
        assert resp.status_code == 401

    def test_request_with_wrong_key_returns_401(self, client):
        """Protected endpoint with an incorrect API key returns 401."""
        resp = _with_header(
            client, "GET", "/api/v1/devices",
            auth_value="Bearer wrong-key-12345",
        )
        assert resp.status_code == 401

    def test_request_with_correct_key_returns_200(self, client):
        """Protected endpoint with valid API key succeeds."""
        resp = client.get("/api/v1/devices")
        assert resp.status_code == 200

    def test_health_endpoint_is_public(self, client):
        """Health check does not require authentication."""
        resp = _unauth_get(client, "/health")
        assert resp.status_code == 200
        data = resp.json()
        assert data["status"] == "healthy"

    def test_version_endpoint_is_public(self, client):
        """Version endpoint does not require authentication."""
        resp = _unauth_get(client, "/api/v1/version")
        assert resp.status_code == 200
        data = resp.json()
        assert "version" in data
        assert "api_version" in data

    def test_post_without_auth_returns_401(self, client):
        """Creating a device without auth fails."""
        resp = _unauth_request(
            client, "POST", "/api/v1/devices",
            json={
                "name": "Unauthorized Device",
                "url": "mock://test",
                "device_type": "mock",
                "led_count": 10,
            },
        )
        assert resp.status_code == 401

    def test_delete_without_auth_returns_401(self, client):
        """Deleting a device without auth fails."""
        resp = _unauth_request(client, "DELETE", "/api/v1/devices/some_id")
        assert resp.status_code == 401

    def test_backup_without_auth_returns_401(self, client):
        """Backup endpoint requires authentication."""
        resp = _unauth_get(client, "/api/v1/system/backup")
        assert resp.status_code == 401

    def test_color_strip_sources_without_auth_returns_401(self, client):
        """Color strip source listing requires authentication."""
        resp = _unauth_get(client, "/api/v1/color-strip-sources")
        assert resp.status_code == 401

    def test_output_targets_without_auth_returns_401(self, client):
        """Output target listing requires authentication."""
        resp = _unauth_get(client, "/api/v1/output-targets")
        assert resp.status_code == 401

    def test_malformed_bearer_token_returns_401_or_403(self, client):
        """A malformed Authorization header is rejected."""
        resp = _with_header(
            client, "GET", "/api/v1/devices",
            auth_value="just-a-key",
        )
        # FastAPI's HTTPBearer returns 403 for malformed format,
        # or 401 depending on auto_error setting. Accept either.
        assert resp.status_code in (401, 403)
