"""Inject the TLS peer cert into ASGI scope — uvicorn ≤ 0.44 does not.

Uvicorn's h11/httptools HTTP protocols build the ASGI ``scope`` dict
without any ``extensions.tls`` entry, so per-request cert pinning
handlers (like POST /swarm/heartbeat) can't see the client cert that
CERT_REQUIRED already validated at handshake.

We patch ``RequestResponseCycle.__init__`` on both protocol modules to
read the peer cert off the asyncio transport (which *does* carry it)
and write the DER bytes into
``scope["extensions"]["tls"]["client_cert_chain"]``. This is the same
key the ASGI TLS extension proposal uses, so the application code will
keep working unchanged if a future uvicorn populates it natively.

Import this module once at app startup time (before uvicorn starts
accepting connections). Idempotent — subsequent imports are no-ops.
"""
from __future__ import annotations

from typing import Any


_PATCHED = False


def _wrap_cycle_init(cycle_cls) -> None:
    original = cycle_cls.__init__

    def _patched_init(self, *args: Any, **kwargs: Any) -> None:
        original(self, *args, **kwargs)
        transport = kwargs.get("transport") or getattr(self, "transport", None)
        if transport is None:
            return
        ssl_obj = transport.get_extra_info("ssl_object")
        if ssl_obj is None:
            return
        try:
            der = ssl_obj.getpeercert(binary_form=True)
        except Exception:
            return
        if not der:
            return
        # scope is a mutable dict uvicorn stores here; Starlette forwards
        # it to handlers as request.scope. Use setdefault so we don't clobber
        # any future native extension entries from uvicorn itself.
        scope = self.scope
        extensions = scope.setdefault("extensions", {})
        extensions.setdefault("tls", {"client_cert_chain": [der]})

    cycle_cls.__init__ = _patched_init


def install() -> None:
    """Patch uvicorn's HTTP cycle classes. Safe to call multiple times."""
    global _PATCHED
    if _PATCHED:
        return
    try:
        from uvicorn.protocols.http import h11_impl
        _wrap_cycle_init(h11_impl.RequestResponseCycle)
    except Exception:  # nosec B110 - optional uvicorn impl may be unavailable
        pass
    try:
        from uvicorn.protocols.http import httptools_impl
        _wrap_cycle_init(httptools_impl.RequestResponseCycle)
    except Exception:  # nosec B110 - optional uvicorn impl may be unavailable
        pass
    _PATCHED = True


# Auto-install on import so simply importing this module patches uvicorn.
install()
