"""PC-04: ``setup_lifespan`` enter+exit cost — baseline-relative (PF-01).

`setup_lifespan` returns an async context manager that runs
`mgf.common.bootstrap` at FastAPI startup. For long-lived workers the cost
amortises to zero; for test fixtures that build fresh apps repeatedly
(PAPER-28's `run_test_app`), the lifespan cost compounds: 1000 tests x 100 ms =
100 s of bootstrap time per suite.

Strategy: build a fresh FastAPI app and enter+exit the lifespan in async
context. Repeat 5x; record the **median** enter+exit wall-clock as a baseline
(PF-03) and compare via ``baselines.assert_no_regression`` (PF-01/07) — NOT the
old absolute 150 ms budget. A breach signals either an mgf.fastapi regression OR
an upstream mgf-common bootstrap regression — cross-reference mgf-common's
``bootstrap_p50_ms`` baseline for the cornerstone.

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

from __future__ import annotations

import asyncio
import time
from typing import TYPE_CHECKING

import pytest
from fastapi import FastAPI

from mgf.fastapi import setup_lifespan

if TYPE_CHECKING:
    from tests.perf.conftest import Baselines

pytestmark = pytest.mark.perf


def test_enter_exit_no_regression(baselines: Baselines) -> None:
    """Median wall-clock across 5 fresh-app enter+exit cycles."""

    async def _drive() -> list[float]:
        runs: list[float] = []
        for _ in range(5):
            app = FastAPI()
            lifespan = setup_lifespan(
                app_name="perf-test",
                app_version="0.0.1",
            )
            cm = lifespan(app)

            start = time.perf_counter()
            await cm.__aenter__()
            await cm.__aexit__(None, None, None)
            runs.append((time.perf_counter() - start) * 1000.0)

        return runs

    runs_ms = asyncio.run(_drive())
    median_ms = sorted(runs_ms)[len(runs_ms) // 2]

    baselines.assert_no_regression(
        "lifespan_enter_exit_p50_ms",
        median_ms,
        units="ms",
        # Cold-start (first run pays bootstrap-state setup) is noisier — wider
        # threshold, matching mgf-common's bootstrap_p50_ms pin.
        threshold_pct=200,
        description="setup_lifespan enter+exit median over 5 fresh-app cycles",
    )
