"""Hot-path benchmark for the model-router classifier.

`classify()` runs on EVERY operator `UserPromptSubmit` (the
`[model-router] tier=… scores=(cheap=N,main=N,hard=N)` line). It is the
hottest per-prompt code path in the harness, so even microseconds compound
across sessions.

Perf-as-test style (mirrors `packages/kg-service/tests/` — stdlib only, no
pytest-benchmark dependency). Prints a hard number (classifications/sec,
µs/call mean, p95) and asserts a loose ceiling so a future regression that
makes the hot path materially slower fails CI rather than silently shipping.

The ceiling is deliberately generous (1500 µs/call mean over the mixed
corpus on a cold box) — it guards against ORDER-OF-MAGNITUDE regressions
(e.g. accidentally recompiling regexes inside the per-rule loop), not
micro-jitter.

Runs under pytest (CI) OR `python3 -m unittest` (offline). Either way the
numbers print to stdout.
"""

from __future__ import annotations

import statistics
import sys
import time
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))

from _loader import load_model_router  # noqa: E402
from prompts import PROMPTS  # noqa: E402

# Mean-per-call ceiling. Generous: catches a 5-10x regression, ignores jitter.
MAX_MEAN_US_PER_CALL = 1500.0

_WARMUP_ROUNDS = 50
_MEASURE_ROUNDS = 400


def measure(classify) -> dict[str, float]:
    # Warm the re-cache + interpreter so first-call compile cost doesn't skew
    # the mean (we measure steady-state per-prompt cost).
    for _ in range(_WARMUP_ROUNDS):
        for p in PROMPTS:
            classify(p)

    per_call_us: list[float] = []
    t_start = time.perf_counter()
    for _ in range(_MEASURE_ROUNDS):
        for p in PROMPTS:
            t0 = time.perf_counter()
            classify(p)
            per_call_us.append((time.perf_counter() - t0) * 1e6)
    wall = time.perf_counter() - t_start

    n = len(per_call_us)
    per_call_us.sort()
    return {
        "calls": float(n),
        "classifications_per_sec": n / wall,
        "us_per_call_mean": statistics.mean(per_call_us),
        "us_per_call_median": statistics.median(per_call_us),
        "us_per_call_p95": per_call_us[int(n * 0.95)],
    }


class TestClassifyBench(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        cls.mr = load_model_router()

    def test_classify_throughput(self) -> None:
        stats = measure(self.mr.classify)
        print(
            "\n[model-router classify bench]"
            f"\n  corpus prompts : {len(PROMPTS)}"
            f"\n  total calls    : {int(stats['calls'])}"
            f"\n  throughput     : {stats['classifications_per_sec']:,.0f} classifications/sec"
            f"\n  µs/call mean   : {stats['us_per_call_mean']:.2f}"
            f"\n  µs/call median : {stats['us_per_call_median']:.2f}"
            f"\n  µs/call p95    : {stats['us_per_call_p95']:.2f}"
        )
        self.assertLess(
            stats["us_per_call_mean"],
            MAX_MEAN_US_PER_CALL,
            f"classify mean {stats['us_per_call_mean']:.1f}µs/call exceeded "
            f"ceiling {MAX_MEAN_US_PER_CALL}µs — hot-path regression?",
        )


if __name__ == "__main__":
    unittest.main()
