"""PC-03: ``_default_user_agent`` per-call cost.

The User-Agent string is computed at every `AsyncHttpClient`
construction:

  1. `importlib.metadata.version("mgf-http")` — walks the entry-
     points database; ~100 µs on first call.
  2. `mgf.common.app_name()` + `mgf.common.app_version()` —
     contextvar reads.
  3. f-string format.

The package-version metadata read dominates. PC-03 memoises that
step (`_lib_version()` is now `@functools.lru_cache(maxsize=1)`),
collapsing the per-call cost from ~107 µs to ~2 µs locally.

The tracker proposes a per-construction budget of ≤ 50 µs.
Note: full `AsyncHttpClient.__init__` is dominated by
`httpx.AsyncClient(...)` (~2.5 ms — connection-pool setup,
TLS context, etc.) which is httpx-side cost outside this gate.
What THIS pin measures is the **mgf-http-owned User-Agent
computation**: that's the surface where the memoisation fix
applies, and where a regression would be detected.

Local baseline (Linux 6.17, Python 3.11, v0.6.0):
  - pre-fix:   ~107 µs/call (importlib.metadata dispatcher tax)
  - post-fix:    ~2 µs/call (lru_cache hits the fast path)

Budget: 50 µs/call — ~25x cushion against post-fix measurement.
A breach signals either the memoisation broke OR a new
metadata read crept into the path.

Marker: ``perf`` (registered in pyproject.toml). Excluded from
the default suite; run via ``pytest -m perf`` for the nightly
sweep.
"""

from __future__ import annotations

import time

import pytest

from mgf.http._client import _default_user_agent, _lib_version

# Per-call budget for `_default_user_agent`. The post-fix
# measurement is ~2 µs; 50 µs gives ~25x cushion for CI noise
# and slower hardware. A breach means either the lru_cache on
# `_lib_version` broke OR a new metadata-read crept into the
# `_default_user_agent` body — investigate
# `src/mgf/http/_client.py`.
_PER_CALL_BUDGET_US = 50.0


@pytest.mark.perf
class TestUserAgentConstruction:
    """Pin the per-call cost of `_default_user_agent`."""

    def test_user_agent_under_budget(self) -> None:
        # Warm-up — pay the first-call lru_cache prime cost
        # before opening the measurement window.
        for _ in range(50):
            _default_user_agent()

        n = 1000
        start = time.perf_counter()
        for _ in range(n):
            _default_user_agent()
        elapsed = time.perf_counter() - start
        per_call_us = (elapsed / n) * 1_000_000

        assert per_call_us < _PER_CALL_BUDGET_US, (
            f"PC-03: _default_user_agent budget breach: "
            f"{per_call_us:.2f} µs/call (budget "
            f"{_PER_CALL_BUDGET_US:.0f} µs). Investigate "
            f"src/mgf/http/_client.py — likely a regression in "
            f"`_lib_version`'s lru_cache or a new metadata read "
            f"in `_default_user_agent`."
        )

    def test_lib_version_is_memoised(self) -> None:
        """`_lib_version` returns the same object across calls.

        Pins the invariant that PC-03 added: the package-version
        read is memoised. If the lru_cache decorator is removed
        or replaced with something that doesn't cache, this
        assertion fails — the test is the contract.
        """
        v1 = _lib_version()
        v2 = _lib_version()
        # `lru_cache` returns the SAME str object on cache hits.
        # If memoisation broke, this would be False.
        assert v1 is v2, (
            "PC-03: `_lib_version` is no longer memoised — the "
            "lru_cache may have been removed. Cached value is the "
            "fix for the ~107 µs / ~2 µs delta in `_default_user_agent`."
        )
