from __future__ import annotations

import argparse
import os
import statistics
import subprocess
import sys
import time
from pathlib import Path


def _default_exe() -> str:
    engine_dir = Path(__file__).resolve().parents[1]
    candidates = [
        engine_dir / "build" / "Release" / "cf_pipeline_v2.exe",
        engine_dir / "build" / "Debug" / "cf_pipeline_v2.exe",
        engine_dir / "build" / "cf_pipeline_v2.exe",
        engine_dir / "cf_pipeline_v2.exe",
        Path("cf_pipeline_v2.exe"),
        Path("cf_pipeline_v2"),
    ]
    for path in candidates:
        if path.exists():
            return str(path)
    return "cf_pipeline_v2"


def _run_once(cmd: list[str]) -> float:
    env = os.environ.copy()
    if env.get("CF_ALLOW_DIRECT_ENGINE", "").strip().lower() not in {"1", "true", "yes", "on"}:
        raise RuntimeError(
            "Direct engine benchmark is policy-gated. "
            "Set CF_ALLOW_DIRECT_ENGINE=1 explicitly to run this tool."
        )
    t0 = time.perf_counter()
    proc = subprocess.run(cmd, check=False, env=env)
    dt = time.perf_counter() - t0
    if proc.returncode != 0:
        raise RuntimeError(f"Command failed ({proc.returncode}): {' '.join(cmd)}")
    return dt


def _run_many(label: str, cmd: list[str], runs: int) -> None:
    samples: list[float] = []
    for _ in range(runs):
        samples.append(_run_once(cmd))
    med = statistics.median(samples)
    p95 = statistics.quantiles(samples, n=20)[-1] if len(samples) >= 2 else samples[0]
    print(f"{label}: median={med:.3f}s p95≈{p95:.3f}s runs={runs}")


def main() -> int:
    parser = argparse.ArgumentParser(description="Benchmark engine + step-internal parallelism controls.")
    parser.add_argument("--exe", default=_default_exe(), help="Path to cf_pipeline_v2 executable")
    parser.add_argument(
        "--pipeline",
        default=str(Path(__file__).resolve().parents[1] / "examples" / "omp_reduction_parallel.jsonld"),
        help="Pipeline JSON-LD (defaults to omp_reduction_parallel.jsonld)",
    )
    parser.add_argument("--runs", type=int, default=3, help="Number of runs per scenario")
    args = parser.parse_args()

    # The pipeline exercises cfbd:OmpReductionStep (cf_basic_dev). Ensure the plugin is built if needed.
    pipeline = str(Path(args.pipeline))

    base = [args.exe, "--pipeline", pipeline]

    try:
        _run_many("safe-default", base + ["--engine-threads", "2"], runs=args.runs)
        _run_many(
            "nested-allowed",
            base
            + [
                "--engine-threads",
                "2",
                "--max-total-threads",
                "16",
                "--default-step-threads",
                "8",
                "--respect-step-parallelism",
                "true",
            ],
            runs=args.runs,
        )
        _run_many(
            "budgeted",
            base
            + [
                "--engine-threads",
                "2",
                "--max-total-threads",
                "8",
                "--default-step-threads",
                "8",
                "--respect-step-parallelism",
                "true",
            ],
            runs=args.runs,
        )
        _run_many(
            "deterministic",
            base
            + [
                "--engine-threads",
                "2",
                "--max-total-threads",
                "8",
                "--default-step-threads",
                "8",
                "--deterministic",
            ],
            runs=args.runs,
        )
    except Exception as exc:
        print(f"bench_parallelism.py error: {exc}", file=sys.stderr)
        return 1

    return 0


if __name__ == "__main__":
    raise SystemExit(main())
