"""test_detect_run.py — run() assembles the 3-bucket coverage map (disjoint) + per-target status + skipped +
coverage_incomplete, and baseline-absent is a RUN-LEVEL hard stop. detect_target stubbed so no gate/LLM runs."""
import importlib.util, os, tempfile

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


def _load(name, path):
    spec = importlib.util.spec_from_file_location(name, path)
    mod = importlib.util.module_from_spec(spec); spec.loader.exec_module(mod); return mod


detect = _load("sg_detect", os.path.join(ROOT, "orchestrator", "detect.py"))
BASELINE = {"id": "baseline", "kind": "llm", "applies_to": {"always": True}}


def _route_repo():
    """Two route files so prioritize/enumerate yield >=2 entries; max_targets=1 forces a budget-dropped bucket."""
    d = tempfile.mkdtemp()
    r = os.path.join(d, "apps/api/src/pages/auth")  # consecutive src/pages -> http-file-route (live FILE_ROUTE_RULES)
    os.makedirs(r, exist_ok=True)
    open(os.path.join(r, "login.ts"), "w").write("export async function POST(req){return new Response('a')}\n")
    open(os.path.join(r, "refresh.ts"), "w").write("export async function POST(req){return new Response('b')}\n")
    return d


def test_run_baseline_absent_is_hard_stop():
    out = detect.run(_route_repo(), [{"id": "auth", "kind": "llm"}], [], out_dir=tempfile.mkdtemp())
    assert out["status"] == "COVERAGE-INCOMPLETE"
    assert "baseline" in out["error"]


def test_run_buckets_disjoint_and_status_present(monkeypatch):
    monkeypatch.setattr(detect, "detect_target",
                        lambda target, kind, dets, *a, **k: {"target": target, "kind": kind,
                                                             "detectors": ["baseline"], "status": "clean",
                                                             "findings": [], "merged_report_path": "/r.md"})
    d = _route_repo()
    cmap = detect.run(d, [BASELINE], [("bad/detector.json", "boom")], out_dir=tempfile.mkdtemp(), max_targets=1)
    scanned = {e["path"] for e in cmap["enumerated_scanned"]}
    dropped = set(cmap["enumerated_budget_dropped"])
    notenum = {e["path"] for e in cmap["not_enumerated"]}
    assert not (scanned & dropped) and not (scanned & notenum) and not (dropped & notenum)  # disjoint
    assert dropped, "max_targets=1 over 2 entries must leave a budget-dropped entry"
    assert cmap["per_target_status"] and cmap["per_target_status"][0]["status"] == "clean"
    assert cmap["skipped_detectors"][0]["reason"] == "boom"
