"""T-AZ-040 (verifier half) — id_token verification: signature,
audience, and expiry, exercised directly against crafted tokens built
with the gateway's exact signer construction (PATTERN-B.md §3)."""

import base64
import hashlib
import hmac
import json
import os
import time

import pytest

from app.services.auth import bw_auth


def make_id_token(secret: str | None = None, **overrides) -> str:
    """Reproduce the gateway's signer (gateway.py _sign_app_token /
    _b64url): compact-JSON claims, base64url with padding stripped,
    hex HMAC-SHA256 keyed on the client_secret."""
    secret = secret if secret is not None else os.environ["BW_CLIENT_SECRET"]
    now = int(time.time())
    claims = {
        "aud": os.environ["BW_CLIENT_ID"],
        "sub": "adi",
        "username": "adi",
        "email": "adi@example.com",
        "first": "Adi",
        "last": "Example",
        "iat": now,
        "exp": now + 300,
    }
    claims.update(overrides)
    value = (
        base64.urlsafe_b64encode(json.dumps(claims, separators=(",", ":")).encode())
        .decode()
        .rstrip("=")
    )
    sig = hmac.new(secret.encode(), value.encode(), hashlib.sha256).hexdigest()
    return f"{value}|{sig}"


def test_t_az_040_valid_token_verifies():
    claims = bw_auth.verify_id_token(make_id_token())
    assert claims["username"] == "adi"
    assert claims["aud"] == os.environ["BW_CLIENT_ID"]
    assert claims["email"] == "adi@example.com"


def test_t_az_040_tampered_signature_rejected():
    token = make_id_token()
    value, sig = token.rsplit("|", 1)
    tampered_sig = ("0" if sig[0] != "0" else "1") + sig[1:]
    with pytest.raises(bw_auth.BWAuthError, match="signature"):
        bw_auth.verify_id_token(f"{value}|{tampered_sig}")


def test_t_az_040_wrong_secret_rejected():
    token = make_id_token(secret="some-other-apps-secret")
    with pytest.raises(bw_auth.BWAuthError, match="signature"):
        bw_auth.verify_id_token(token)


def test_t_az_040_tampered_payload_rejected():
    token = make_id_token()
    value, sig = token.rsplit("|", 1)
    other = make_id_token(username="mallory", sub="mallory").rsplit("|", 1)[0]
    with pytest.raises(bw_auth.BWAuthError, match="signature"):
        bw_auth.verify_id_token(f"{other}|{sig}")


def test_t_az_040_wrong_audience_rejected():
    # Correctly signed with OUR secret but minted for another client_id.
    token = make_id_token(aud="someotherapp")
    with pytest.raises(bw_auth.BWAuthError, match="audience"):
        bw_auth.verify_id_token(token)


def test_t_az_040_expired_token_rejected():
    now = int(time.time())
    token = make_id_token(iat=now - 600, exp=now - 300)
    with pytest.raises(bw_auth.BWAuthError, match="expired"):
        bw_auth.verify_id_token(token)


def test_t_az_040_malformed_token_rejected():
    for bad in ("", "no-pipe-here", "just|garbage", None):
        with pytest.raises(bw_auth.BWAuthError):
            bw_auth.verify_id_token(bad)
