"""Resolve the forge read credential (STATE-WP-0084-T03, MASON-WP-0003-T05).

Three sources, tried in order: a mounted file, an environment variable, and
OpenBao via Kubernetes auth. Production uses the third — this cluster has no
agent injector and no secrets-store CSI driver, so the pod authenticates with
its projected ServiceAccount token and reads the KV path itself, which is what
`MASON-WP-0003-T02` built the Kubernetes auth role for. The first two exist so
the code is runnable and testable outside the cluster.

**Absent is a supported state, never an error.** A hub with no credential still
derives every public repository; only private ones become unreadable, and
`STATE-WP-0084-T01` already reports that as its own named condition rather than
as records that stopped deriving. So every failure here — no configuration, no
network, OpenBao down, permission denied — resolves to `None`. Raising would
convert "cannot read nine repositories" into "the whole pass failed".
"""

from __future__ import annotations

import logging
import os
import time
from pathlib import Path

import httpx

logger = logging.getLogger(__name__)

TOKEN_ENV = "FORGE_READ_TOKEN"
TOKEN_FILE_ENV = "FORGE_READ_TOKEN_FILE"
OPENBAO_ADDR_ENV = "OPENBAO_ADDR"
OPENBAO_ROLE_ENV = "OPENBAO_K8S_ROLE"
OPENBAO_JWT_PATH_ENV = "OPENBAO_K8S_TOKEN_PATH"
OPENBAO_AUTH_MOUNT_ENV = "OPENBAO_K8S_AUTH_MOUNT"
SECRET_PATH_ENV = "FORGE_READ_SECRET_PATH"
SECRET_KEY_ENV = "FORGE_READ_SECRET_KEY"

# Long enough that a fleet reset of 121 repositories does not re-authenticate
# 121 times; short enough that a rotated token is picked up without a redeploy,
# which is what MASON-WP-0003-T02 requires of this lane.
CACHE_TTL_SECONDS = 300.0

_cache: tuple[float, str | None] | None = None


def reset_cache() -> None:
    global _cache
    _cache = None


def _from_file() -> str | None:
    path = os.environ.get(TOKEN_FILE_ENV)
    if not path:
        return None
    try:
        return Path(path).read_text(encoding="utf-8").strip() or None
    except OSError:
        # Deliberately not falling through to the environment: a broken mount
        # that silently used a stale value would look like success.
        logger.warning("forge credential: token file %s is unreadable", path)
        return None


def _from_env() -> str | None:
    return (os.environ.get(TOKEN_ENV) or "").strip() or None


def _from_openbao() -> str | None:
    addr = (os.environ.get(OPENBAO_ADDR_ENV) or "").strip().rstrip("/")
    secret_path = (os.environ.get(SECRET_PATH_ENV) or "").strip().strip("/")
    role = (os.environ.get(OPENBAO_ROLE_ENV) or "").strip()
    jwt_path = os.environ.get(OPENBAO_JWT_PATH_ENV) or "/var/run/secrets/openbao/token"
    mount = (os.environ.get(OPENBAO_AUTH_MOUNT_ENV) or "kubernetes").strip("/")
    key = (os.environ.get(SECRET_KEY_ENV) or "token").strip()
    if not (addr and secret_path and role):
        return None
    try:
        jwt = Path(jwt_path).read_text(encoding="utf-8").strip()
    except OSError:
        logger.warning("forge credential: no ServiceAccount token at %s", jwt_path)
        return None
    try:
        with httpx.Client(timeout=10.0) as client:
            login = client.post(
                f"{addr}/v1/auth/{mount}/login", json={"role": role, "jwt": jwt}
            )
            login.raise_for_status()
            client_token = login.json()["auth"]["client_token"]
            read = client.get(
                f"{addr}/v1/{secret_path}", headers={"X-Vault-Token": client_token}
            )
            read.raise_for_status()
            data = read.json()["data"]
            # KV v2 nests the payload under a second "data"; v1 does not.
            if isinstance(data.get("data"), dict):
                data = data["data"]
    except (httpx.HTTPError, KeyError, ValueError) as exc:
        # Never include the response body: a failed KV read can echo content.
        logger.warning("forge credential: OpenBao lookup failed (%s)", type(exc).__name__)
        return None
    value = data.get(key)
    if not isinstance(value, str) or not value.strip():
        logger.warning("forge credential: key %r absent at the KV path", key)
        return None
    return value.strip()


def forge_read_token(*, use_cache: bool = True) -> str | None:
    """The forge read credential, or `None` if this instance has none."""
    global _cache
    now = time.monotonic()
    if use_cache and _cache is not None and now - _cache[0] < CACHE_TTL_SECONDS:
        return _cache[1]
    if os.environ.get(TOKEN_FILE_ENV):
        # Configured to use a file means *that* file and nothing else. Falling
        # back would let a broken mount quietly resolve to a stale environment
        # value that nobody knows is in use.
        token = _from_file()
    else:
        token = _from_env() or _from_openbao()
    _cache = (now, token)
    return token
