"""PC-04: ``create_sessionmaker`` construction cost pin.

`create_sessionmaker(engine)` is a thin wrapper around
:class:`async_sessionmaker`. Its work:

  1. RA-03 collision check on `**extra` (a frozenset
     intersection — sub-µs).
  2. `async_sessionmaker(engine, class_=AsyncSession,
     expire_on_commit=expire_on_commit, **extra)` — frozen-state
     construction.

For test fixtures that build a fresh sessionmaker per test
(or per `with`-block) the cost compounds; for production
workers it's a one-shot at startup.

Strategy: build N sessionmakers from a shared engine and pin
the per-call cost. The engine itself is built once outside
the measurement window so we isolate the sessionmaker-only
cost.

Local baseline (Linux 6.17, Python 3.11, v0.2.0):
  - ~5-15 µs/construction.

Tracker proposed a 100 µs/construction budget. Local
measurement is well under that; the budget is the
regression-gate.

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 asyncio
import time

import pytest

from mgf.sqlalchemy import create_engine, create_sessionmaker

# Per-construction budget. The engine is built ONCE outside the
# measurement window; this measures only the sessionmaker wrapper
# cost. 100 µs is generous; local measurement is in the 5-15 µs
# range.
_PER_CALL_BUDGET_US = 100.0

_N = 100


@pytest.mark.perf
class TestSessionmakerConstructionCost:
    """Pin the per-call construction cost of `create_sessionmaker`."""

    def test_construction_under_budget(self) -> None:
        """`create_sessionmaker(engine)` stays under budget.

        Builds N sessionmakers from a shared engine. The engine
        cost is excluded (built once before the measurement window).
        """

        async def _drive() -> float:
            engine = create_engine("sqlite+aiosqlite:///:memory:")
            try:
                # Warm-up.
                for _ in range(20):
                    create_sessionmaker(engine)

                start = time.perf_counter()
                for _ in range(_N):
                    create_sessionmaker(engine)
                elapsed = time.perf_counter() - start
                return (elapsed / _N) * 1_000_000
            finally:
                await engine.dispose()

        per_call_us = asyncio.run(_drive())

        assert per_call_us < _PER_CALL_BUDGET_US, (
            f"PC-04: create_sessionmaker budget breach: "
            f"{per_call_us:.2f} us/construction (budget "
            f"{_PER_CALL_BUDGET_US:.0f} us). Investigate "
            f"src/mgf/sqlalchemy/_session.py::create_sessionmaker - "
            f"likely a regression in the RA-03 collision check or "
            f"the async_sessionmaker forwarding."
        )
