"""PC-03: ``create_engine`` construction cost pin.

`create_engine(url)` allocates an `AsyncEngine` without connecting:

  1. URL parse (SQLAlchemy ``make_url``).
  2. Dialect load (for ``sqlite+aiosqlite``, this imports
     :mod:`aiosqlite`).
  3. Pool / engine instantiation.

For test fixtures that build a fresh engine per test (strict-
isolation patterns) the cost compounds; for production workers
it's a one-shot at startup. Either way we want a pin so a
regression in URL parse / dialect resolution surfaces here.

Strategy: build 100 engines back-to-back and measure per-call
cost. The first construction pays the one-time dialect import
cost; we warm up with a single construction so the measurement
captures the steady-state per-engine cost.

Local baseline (Linux 6.17, Python 3.11, v0.2.0):
  - sqlite+aiosqlite: ~30-60 µs/construction (post-warmup).

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

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

# Per-construction budget. EXCLUDES the one-time dialect import
# (warmed up before the measurement window). 200 µs is generous;
# local measurement is in the 30-60 µs range, so a breach signals
# real per-call work crept in.
_PER_CALL_BUDGET_US = 200.0

_N = 100


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

    def test_construction_under_budget(self) -> None:
        """`create_engine('sqlite+aiosqlite:///:memory:')` stays under budget.

        Builds N engines back-to-back. Each `create_engine` call is
        a fresh `AsyncEngine` — we dispose at the end of the run to
        return pool/driver resources, but the construction cost is
        what we're pinning, not teardown.
        """

        async def _drive() -> float:
            # Warm-up: amortise the one-time dialect import +
            # interpreter cache fill.
            warmup = create_engine("sqlite+aiosqlite:///:memory:")
            await warmup.dispose()

            engines = []
            start = time.perf_counter()
            for _ in range(_N):
                engines.append(create_engine("sqlite+aiosqlite:///:memory:"))
            elapsed = time.perf_counter() - start

            # Cleanup — return driver/pool resources before exit.
            for e in engines:
                await e.dispose()
            return (elapsed / _N) * 1_000_000

        per_call_us = asyncio.run(_drive())

        assert per_call_us < _PER_CALL_BUDGET_US, (
            f"PC-03: create_engine budget breach: "
            f"{per_call_us:.2f} us/construction (budget "
            f"{_PER_CALL_BUDGET_US:.0f} us). Investigate "
            f"src/mgf/sqlalchemy/_engine.py::create_engine - "
            f"likely a regression in URL parse, dialect resolution, "
            f"or kwarg-assembly logic."
        )
