"""PC-06: matcher DSL evaluation cost.

The matcher DSL (``core/matchers/{primitives,format,semantic}.py``)
is the per-finding hot path. A schema check might compose 20-50
matcher evaluations per probe response; for a 1000-probe suite
that's 20-50k matcher invocations.

Per-matcher budget: ≤ 5 µs. Primitives (equals, gt, in_) are
sub-µs; format matchers (is_uuid, is_email) are µs because they
run a regex; semantic matchers (is_iban, is_imei) are µs because
they run real validation logic.

This test exercises each matcher class with 10 000 evaluations and
asserts the per-call median stays under the budget.

Marker: ``perf``. Run via ``pytest -m perf``.
"""

from __future__ import annotations

import time

import pytest

from mgf.apiprobe import (
    equals,
    gt,
    in_,
    is_e164,
    is_email,
    is_iban,
    is_imei,
    is_iso8601_z,
    is_url,
    is_uuid,
    matches,
)

pytestmark = pytest.mark.perf

_ITERATIONS = 10_000

# Per-matcher budget. Primitives should be sub-µs; format and
# semantic matchers around 1-3 µs. 5 µs is generous (~2-5x slack
# against local measurement) — leaves headroom for CI variance.
_PER_CALL_BUDGET_US = 5.0


# (matcher, sample value) pairs. The value is what the matcher gets
# invoked on; we measure the matcher's call cost, not the value
# construction.
_MATCHER_SAMPLES = [
    ("equals", equals(200), 200),
    ("gt", gt(100), 200),
    ("in_", in_([200, 201, 204]), 201),
    ("matches", matches(r"^[A-Z]{3}-[0-9]{4}$"), "ABC-1234"),
    ("is_uuid", is_uuid(), "f47ac10b-58cc-4372-a567-0e02b2c3d479"),
    ("is_iso8601_z", is_iso8601_z(), "2026-05-17T12:00:00Z"),
    ("is_url", is_url(), "https://example.com/api/v1/users"),
    ("is_email", is_email(), "user@example.com"),
    ("is_iban", is_iban(), "GB82WEST12345698765432"),
    ("is_imei", is_imei(), "490154203237518"),
    ("is_e164", is_e164(), "+14155552671"),
]


@pytest.mark.parametrize(
    ("name", "matcher", "sample"),
    _MATCHER_SAMPLES,
    ids=[name for name, _, _ in _MATCHER_SAMPLES],
)
def test_matcher_per_call_under_budget(name, matcher, sample) -> None:
    # Warm up — first call may include caching of compiled regex.
    matcher(sample)

    start = time.perf_counter()
    for _ in range(_ITERATIONS):
        matcher(sample)
    elapsed_s = time.perf_counter() - start
    per_call_us = (elapsed_s / _ITERATIONS) * 1e6

    assert per_call_us < _PER_CALL_BUDGET_US, (
        f"PC-06: matcher {name!r} per-call cost is "
        f"{per_call_us:.2f} µs (budget {_PER_CALL_BUDGET_US:.1f} µs). "
        f"For a 1000-probe suite with 50 matchers per response, "
        f"that's {per_call_us * 1000 * 50 / 1000:.1f} ms of matcher "
        f"overhead per run — visible. Investigate "
        f"src/mgf/apiprobe/core/matchers/."
    )
