import time

import jwt
from httpx import AsyncClient
from jwt import PyJWK

import app.core.auth as auth_module


def make_token(private_key, *, sub="user_1", org_id="org_1", exp_delta=900, iss=None, aud=None):
    now = int(time.time())
    payload = {
        "sub": sub,
        "email": "a@b.c",
        "org_id": org_id,
        "iat": now,
        "exp": now + exp_delta,
        "iss": iss or "http://test-auth.local",
        "aud": aud or "http://test-auth.local",
    }
    return jwt.encode(payload, private_key, algorithm="EdDSA", headers={"kid": "test-key"})


async def test_me_with_valid_token(client: AsyncClient, test_private_key) -> None:
    resp = await client.get(
        "/me", headers={"Authorization": f"Bearer {make_token(test_private_key)}"}
    )
    assert resp.status_code == 200
    assert resp.json() == {"user_id": "user_1", "email": "a@b.c", "org_id": "org_1"}


async def test_me_without_org(client: AsyncClient, test_private_key) -> None:
    token = make_token(test_private_key, org_id=None)
    resp = await client.get("/me", headers={"Authorization": f"Bearer {token}"})
    assert resp.status_code == 200
    assert resp.json()["org_id"] is None


async def test_me_without_token(client: AsyncClient) -> None:
    resp = await client.get("/me")
    assert resp.status_code == 401
    assert resp.headers["content-type"].startswith("application/problem+json")


async def test_me_without_token_sets_www_authenticate(client: AsyncClient) -> None:
    resp = await client.get("/me")
    assert resp.status_code == 401
    assert resp.headers["www-authenticate"] == "Bearer"


async def test_me_expired_token(client: AsyncClient, test_private_key) -> None:
    token = make_token(test_private_key, exp_delta=-10)
    resp = await client.get("/me", headers={"Authorization": f"Bearer {token}"})
    assert resp.status_code == 401


async def test_me_wrong_issuer(client: AsyncClient, test_private_key) -> None:
    token = make_token(test_private_key, iss="http://evil.local")
    resp = await client.get("/me", headers={"Authorization": f"Bearer {token}"})
    assert resp.status_code == 401


async def test_me_wrong_key(client: AsyncClient) -> None:
    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey

    token = make_token(Ed25519PrivateKey.generate())
    resp = await client.get("/me", headers={"Authorization": f"Bearer {token}"})
    assert resp.status_code == 401


class _BrokenJwksClient:
    """JWKS injoignable : `PyJWKClientConnectionError` à chaque appel."""

    def get_signing_key_from_jwt(self, _token: str) -> PyJWK:
        raise jwt.PyJWKClientConnectionError("JWKS injoignable")


async def test_me_jwks_unreachable(client: AsyncClient, test_private_key, monkeypatch) -> None:
    # `monkeypatch` restaure le faux client normal à la fin du test.
    monkeypatch.setattr(auth_module, "_jwks_client", _BrokenJwksClient())
    resp = await client.get(
        "/me", headers={"Authorization": f"Bearer {make_token(test_private_key)}"}
    )
    assert resp.status_code == 503
    assert resp.headers["content-type"].startswith("application/problem+json")
    assert resp.json()["title"] == "Service Unavailable"
