from __future__ import annotations

import base64
import hashlib
import hmac
import json
import logging
from collections.abc import Mapping

from ember.types import JSONValue

from .settings import get_gateway_settings

_LOG = logging.getLogger(__name__)


def _b64url(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).rstrip(b"=").decode("ascii")


def _b64url_json(obj: Mapping[str, JSONValue]) -> str:
    return _b64url(json.dumps(obj, separators=(",", ":"), sort_keys=True).encode("utf-8"))


def _signing_secret() -> bytes:
    secret = get_gateway_settings().secrets.signing_secret
    if not secret:
        raise RuntimeError("gateway.secrets.signing_secret must be configured")
    return secret.encode("utf-8")


def jws_sign_hs256(claims: Mapping[str, JSONValue]) -> str:
    header = {"alg": "HS256", "typ": "JWT"}
    b64_header = _b64url_json(header)
    b64_payload = _b64url_json(claims)
    signing_input = f"{b64_header}.{b64_payload}".encode("ascii")
    sig = hmac.new(_signing_secret(), signing_input, hashlib.sha256).digest()
    b64_sig = _b64url(sig)
    return f"{b64_header}.{b64_payload}.{b64_sig}"


def jws_verify_hs256(token: str) -> tuple[bool, dict[str, JSONValue]]:
    secret = _signing_secret()
    try:
        header_b64, payload_b64, sig_b64 = token.split(".")
        signing_input = f"{header_b64}.{payload_b64}".encode("ascii")
        expected = hmac.new(secret, signing_input, hashlib.sha256).digest()
        provided = base64.urlsafe_b64decode(sig_b64 + "==")
        if not hmac.compare_digest(expected, provided):
            return False, {}
        payload_json = base64.urlsafe_b64decode(payload_b64 + "==").decode("utf-8")
        claims = json.loads(payload_json)
        return True, claims
    except Exception as exc:  # pragma: no cover
        _LOG.warning("Failed to verify RBC token", exc_info=exc)
        return False, {}
