"""test_trigger_precision.py — the user's go-live invariant, measured against the REAL registry (not fixtures):
'the payments gate must not fire at any random README commit'. Two structural guarantees, asserted on the
production manifests:
  1. PREVENT (commit-time) is DETERMINISTIC-ONLY — the finance/payment detector is an LLM (DETECT) detector,
     so a README (or any) commit NEVER selects it in the pre-commit path.
  2. DETECT (post-commit LLM) routes finance by money SIGNAL — a money-free file (README, image-upload, emoji
     picker) does NOT select finance; a money file DOES (positive control).
Pure + deterministic; no LLM invoked.
"""
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"))
mapper = _load("sg_mapper", os.path.join(ROOT, "orchestrator", "mapper.py"))
registry = _load("sg_registry", os.path.join(ROOT, "prevent", "registry.py"))

DETECTORS, _ = registry.load(ROOT)


def _tmp(text, suffix):
    fd, p = tempfile.mkstemp(suffix=suffix)
    with os.fdopen(fd, "w") as fh:
        fh.write(text)
    return p


def _detect_ids(rel, body):
    """Production DETECT routing for one file: kind from path+content, then select_detectors."""
    p = _tmp(body, os.path.splitext(rel)[1] or ".txt")
    kind = mapper.kind_of(rel, body)
    sel = detect.select_detectors(p, kind, DETECTORS) or []
    return {d["id"] for d in sel}


# ---- guarantee 1: PREVENT (commit-time) never selects an LLM/finance detector, for ANY file ----

def test_prevent_commit_selects_no_llm_detector_on_readme():
    # a README commit: the pre-commit path selects ONLY deterministic detectors (scope per-file/repo). finance
    # (and every LLM detector) is absent by construction -> the payments gate cannot fire at commit time.
    for trig in ("pre-commit", "pre-edit"):
        sel = registry.applicable_per_file(DETECTORS, "README.md", trig) + \
              registry.applicable_repo(DETECTORS, ["README.md"], trig)
        llm = [d["id"] for d in sel if d.get("kind") == "llm"]
        assert llm == [], f"PREVENT must select NO LLM detector on README ({trig}); got {llm}"
        assert all(d["id"] != "finance" for d in sel), f"finance must never be in the commit path; got {sel}"


def test_prevent_commit_path_is_deterministic_only_for_any_file():
    # the floor invariant (ARCHITECTURE §10.1): the commit-time selection is deterministic-only, never an LLM band.
    for rel in ("README.md", "docs/guide.md", "apps/web/src/components/EmojiPicker.tsx",
                "apps/web/src/routes/upload/image.ts"):
        for trig in ("pre-commit", "pre-edit"):
            sel = registry.applicable_per_file(DETECTORS, rel, trig) + \
                  registry.applicable_repo(DETECTORS, [rel], trig)
            assert all(d.get("kind") != "llm" for d in sel), f"{rel} ({trig}) selected an LLM detector: {sel}"


# ---- guarantee 2: DETECT routes finance by money SIGNAL, not blanket ----

def test_detect_finance_not_selected_on_money_free_files():
    # the emoji picker / image upload / README have NO money signal -> finance is NOT added (signal-gated ADD).
    cases = {
        "README.md": "# My Project\nInstall with pnpm. Run the dev server. Contributions welcome.\n",
        "apps/web/src/components/EmojiPicker.tsx": "export function EmojiPicker(){ return <div>😀😀</div> }\n",
        "apps/web/src/routes/upload/image.ts": "export async function onRequestPost(ctx){ const f=await ctx.request.formData(); return store(f.get('image')) }\n",
    }
    for rel, body in cases.items():
        ids = _detect_ids(rel, body)
        assert "finance" not in ids, f"finance must NOT route on money-free {rel}; got {ids}"


def test_detect_finance_selected_on_money_file_positive_control():
    # positive control: a file that DOES move money -> finance IS added. Proves the negative tests above are
    # gated by signal-absence, not a broken matcher that never selects finance.
    body = "export async function onRequestPost(ctx){ return refund({ amount, currency, payout: true }) }\n"
    ids = _detect_ids("apps/api/src/routes/billing/refund.ts", body)
    assert "finance" in ids, f"finance MUST route on a money file (positive control); got {ids}"
