"""Live app-session check: Core asks Cloud whether (sub, sid) is still active (gate 3.4).

Transport only. Core mints a short-lived auth_session.introspect capability bound to the
project, then POSTs {sub, sid}. Cloud answers {"active": true|false}. Anything other than an
explicit active=true denies. A transport failure raises, so the caller can answer 503 instead of
mislabelling a Cloud outage as a revoked session.
"""

from __future__ import annotations

import time
from urllib.parse import quote
from uuid import uuid4

import httpx
from jose import jwt

CORE_ISSUER = "p2hs-core"
CORE_AUDIENCE = "p2hs-cloud-auth-projection"
INTROSPECT_CAPABILITY = "auth_session.introspect"
CAPABILITY_TTL_SECONDS = 120
_MIN_SECRET_LENGTH = 32


class SessionCheckUnavailable(Exception):
    """Cloud could not be reached or answered something unusable. Not a denial."""


def mint_introspection_capability(
    project_id: str,
    secret: str,
    *,
    now: float | None = None,
    ttl_seconds: int = CAPABILITY_TTL_SECONDS,
) -> str:
    if len(secret) < _MIN_SECRET_LENGTH:
        raise ValueError("auth_projection_secret_too_short")
    issued = int(now if now is not None else time.time())
    claims = {
        "iss": CORE_ISSUER,
        "aud": CORE_AUDIENCE,
        "capability": INTROSPECT_CAPABILITY,
        "project_id": project_id,
        "iat": issued,
        "exp": issued + ttl_seconds,
        "jti": uuid4().hex,
    }
    return jwt.encode(claims, secret, algorithm="HS256")


async def session_is_active(
    base_url: str,
    secret: str,
    project_id: str,
    *,
    cloud_app_user_id: str,
    session_id: str,
    transport: httpx.AsyncBaseTransport | None = None,
    timeout_seconds: float = 5.0,
) -> bool:
    if not base_url:
        raise SessionCheckUnavailable("cloud_control_api_url_not_configured")
    token = mint_introspection_capability(project_id, secret)
    url = (
        f"{base_url.rstrip('/')}/v1/cloud/core/projects/{quote(project_id, safe='')}"
        "/auth/sessions/introspect"
    )
    try:
        async with httpx.AsyncClient(transport=transport, timeout=timeout_seconds) as client:
            response = await client.post(
                url,
                json={"sub": cloud_app_user_id, "sid": session_id},
                headers={"Authorization": f"Bearer {token}"},
            )
    except httpx.HTTPError as exc:
        raise SessionCheckUnavailable("transport_error") from exc

    if response.status_code != 200:
        # A rejected capability is a Core configuration problem, not a valid answer.
        raise SessionCheckUnavailable(f"http_{response.status_code}")
    try:
        body = response.json()
    except ValueError as exc:
        raise SessionCheckUnavailable("invalid_response") from exc
    return isinstance(body, dict) and body.get("active") is True
