"""A mock OAuth2 client-credentials IdP, for tests and local demos only.

Stands in for a real OAuth2/OIDC identity provider (Auth0/Keycloak/Okta).
Issues HS256 JWTs signed with a fixed shared secret -- pair it with
a2a_passport.auth.verifiers.SharedSecretVerifier(JWT_SECRET) on the server
side. This is deliberately NOT part of the production auth story: real
deployments point OAuth2ClientCredentialsProvider at a real IdP's token
endpoint and JWKSVerifier at its JWKS endpoint instead of importing anything
from this module.
"""
from __future__ import annotations

import time

import jwt
from fastapi import FastAPI, Form, HTTPException
from fastapi.responses import JSONResponse

JWT_SECRET = "test-only-shared-secret-do-not-use-in-prod"
JWT_ALGORITHM = "HS256"
TOKEN_TTL_SECONDS = 120

# client_id -> {secret, allowed: {audience -> scope}}. In a real IdP this
# registry lives in the IdP itself, configured by whoever runs it.
CLIENT_REGISTRY: dict[str, dict] = {}


def register_client(client_id: str, secret: str, allowed: dict[str, str]) -> None:
    """Register a test client and the audiences/scopes it may request."""
    CLIENT_REGISTRY[client_id] = {"secret": secret, "allowed": allowed}


def build_mock_idp_app() -> FastAPI:
    """Build a fresh mock IdP FastAPI app bound to the current CLIENT_REGISTRY."""
    app = FastAPI(title="a2a-passport mock IdP (testing only)")

    @app.post("/token")
    async def issue_token(
        grant_type: str = Form(...),
        client_id: str = Form(...),
        client_secret: str = Form(...),
        audience: str = Form(...),
    ):
        if grant_type != "client_credentials":
            raise HTTPException(status_code=400, detail="unsupported_grant_type")

        client = CLIENT_REGISTRY.get(client_id)
        if not client or client["secret"] != client_secret:
            raise HTTPException(status_code=401, detail="invalid_client")

        scope = client["allowed"].get(audience)
        if not scope:
            raise HTTPException(
                status_code=403,
                detail=f"client '{client_id}' is not authorized for audience '{audience}'",
            )

        now = int(time.time())
        claims = {
            "sub": client_id,
            "aud": audience,
            "scope": scope,
            "iat": now,
            "exp": now + TOKEN_TTL_SECONDS,
        }
        token = jwt.encode(claims, JWT_SECRET, algorithm=JWT_ALGORITHM)
        return JSONResponse(
            {
                "access_token": token,
                "token_type": "Bearer",
                "expires_in": TOKEN_TTL_SECONDS,
                "scope": scope,
            }
        )

    return app


if __name__ == "__main__":
    import uvicorn

    register_client(
        "orchestrator-agent",
        "orchestrator-secret",
        {"weather-agent": "weather:read", "translator-agent": "translator:use"},
    )
    register_client(
        "external-caller", "external-caller-secret", {"orchestrator-agent": "orchestrator:invoke"}
    )
    uvicorn.run(build_mock_idp_app(), host="127.0.0.1", port=9000)
