"""Integration tests for PassportAuthMiddleware against a minimal FastAPI
app -- independent of any real A2A agent, so failures here point straight
at the middleware rather than at a2a-sdk internals.
"""
from __future__ import annotations

import httpx
import pytest
from fastapi import FastAPI

from a2a_passport.auth.base import VerifiedClaims
from a2a_passport.auth.middleware import PassportAuthMiddleware
from a2a_passport.exceptions import TokenVerificationError


class _StubVerifier:
    """Deterministic verifier for middleware tests: no real crypto involved."""

    def __init__(self):
        self.valid_tokens: dict[str, VerifiedClaims] = {}

    def register(self, token: str, claims: VerifiedClaims) -> None:
        self.valid_tokens[token] = claims

    async def verify(self, token: str, *, audience: str) -> VerifiedClaims:
        if token not in self.valid_tokens:
            raise TokenVerificationError("unknown token", error_code="invalid_token")
        return self.valid_tokens[token]


@pytest.fixture
def verifier():
    return _StubVerifier()


@pytest.fixture
def app(verifier):
    app = FastAPI()

    @app.get("/.well-known/agent-card.json")
    async def card():
        return {"name": "stub-agent"}

    @app.get("/protected")
    async def protected():
        return {"ok": True}

    app.add_middleware(
        PassportAuthMiddleware,
        verifier=verifier,
        expected_audience="stub-agent",
        required_scope="stub:invoke",
    )
    return app


@pytest.fixture
async def client(app):
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as c:
        yield c


async def test_card_endpoint_is_public(client):
    r = await client.get("/.well-known/agent-card.json")
    assert r.status_code == 200


async def test_missing_token_rejected(client):
    r = await client.get("/protected")
    assert r.status_code == 401
    assert r.json()["error"] == "missing_bearer_token"


async def test_invalid_token_rejected(client):
    r = await client.get("/protected", headers={"Authorization": "Bearer garbage"})
    assert r.status_code == 401
    assert r.json()["error"] == "invalid_token"


async def test_insufficient_scope_rejected(client, verifier):
    verifier.register(
        "good-token",
        VerifiedClaims(subject="caller", audience="stub-agent", scopes=frozenset({"stub:other"})),
    )
    r = await client.get("/protected", headers={"Authorization": "Bearer good-token"})
    assert r.status_code == 403
    assert r.json()["error"] == "insufficient_scope"


async def test_valid_token_with_scope_succeeds(client, verifier):
    verifier.register(
        "good-token",
        VerifiedClaims(subject="caller", audience="stub-agent", scopes=frozenset({"stub:invoke"})),
    )
    r = await client.get("/protected", headers={"Authorization": "Bearer good-token"})
    assert r.status_code == 200
    assert r.json() == {"ok": True}
