#!/usr/bin/env python3
"""PAPER-100 Phase-3 helper: re-baseline a repo's §Z ledger to the 258-MUST set.

Appends the 40 newly-indexed MUSTs (TM/PF/OB/NM/GUI/REL-10..13 — the families PAPER-100 added to
the Index of MUSTs) to a repo's ``docs/inprogress/MGF_STANDARDS_CONFORMANCE.md`` with
shape-appropriate verdicts auto-detected from the repo, **idempotently** (skips ids already
present). Flags the genuine judgment calls (Qt-import → GUI-01; perf-without-baselines → PF) for
human review before commit.

Usage (from anywhere; resolves repos under $MGF_ROOT or ~/PycharmProjects):
    python scripts/seed_ledger_rebaseline.py mgf-http mgf-vm-core ...

Verdict rules (see docs/archive/2026/PAPER-100-rollout.md for the full rationale):
  NM-01/02/03 ✅ · REL-10/11/13 ✅ · REL-12 N/A · GUI-02..10 N/A · OB-01..11 N/A (libraries consume
  mgf.common.observability — OB rules bind the owner) · GUI-01 ✅ if no Qt import else FLAG · TM-01/02/04
  ✅ if the repo uses datetime (mirrors its DP-12 ✅) else N/A · TM-03 N/A · TM-07 N/A · TM-08 ✅ if async
  else N/A · PF all N/A if no tests/perf/; else PF-02/07 ✅, PF-04/08 N/A, PF-01/03/05 ✅ if
  tests/perf/baselines.json else ⏳ (FLAG).
"""

from __future__ import annotations

import os
import re
import subprocess
import sys
from pathlib import Path

NEW_IDS = [
    "TM-01",
    "TM-02",
    "TM-03",
    "TM-04",
    "TM-07",
    "TM-08",
    "PF-01",
    "PF-02",
    "PF-03",
    "PF-04",
    "PF-05",
    "PF-07",
    "PF-08",
    "OB-01",
    "OB-02",
    "OB-03",
    "OB-04",
    "OB-05",
    "OB-07",
    "OB-08",
    "OB-09",
    "OB-10",
    "OB-11",
    "NM-01",
    "NM-02",
    "NM-03",
    "GUI-01",
    "GUI-02",
    "GUI-03",
    "GUI-04",
    "GUI-05",
    "GUI-06",
    "GUI-07",
    "GUI-08",
    "GUI-09",
    "GUI-10",
    "REL-10",
    "REL-11",
    "REL-12",
    "REL-13",
]


def _federation_root() -> Path:
    return Path(os.environ.get("MGF_ROOT", str(Path.home() / "PycharmProjects")))


def _grep(repo: Path, pattern: str) -> int:
    src = repo / "src"
    if not src.exists():
        return 0
    r = subprocess.run(["grep", "-rlE", pattern, str(src)], capture_output=True, text=True)
    return len([x for x in r.stdout.splitlines() if x.strip()])


def verdicts(repo: Path) -> tuple[dict[str, tuple[str, str]], list[str]]:
    qt = _grep(repo, r"PySide|PyQt")
    dt = _grep(repo, r"datetime|isoformat")
    asy = _grep(repo, r"async def|asyncio")
    perf = (repo / "tests" / "perf").is_dir()
    baselines = (repo / "tests" / "perf" / "baselines.json").is_file()
    flags: list[str] = []
    v: dict[str, tuple[str, str]] = {
        "NM-01": ("✅", "distribution name is hyphenated (NM-01)."),
        "NM-02": ("✅", "dotted import namespace `mgf.<sibling>` (NM-02)."),
        "NM-03": ("✅", "Codeberg slug hyphenated (NM-03)."),
        "REL-10": (
            "✅",
            "federation member; participates in cohort-walk releases on a mgf-common pin break.",
        ),
        "REL-11": ("✅", "released via the Phase A/B cohort-release flow."),
        "REL-12": (
            "N/A",
            "binds the cohort Phase-A *agent's* pytest-denial handling, not a released sibling's code.",
        ),
        "REL-13": (
            "✅",
            "releases pass the federation consistency gate (`mgf-fed registry --check`).",
        ),
        "TM-07": ("N/A", "UTC only; no IANA timezone-name usage."),
        "TM-03": (
            "N/A",
            "no duration measurements with a wall clock (timeouts are not timed spans).",
        ),
    }
    for g in (
        "GUI-02",
        "GUI-03",
        "GUI-04",
        "GUI-05",
        "GUI-06",
        "GUI-07",
        "GUI-08",
        "GUI-09",
        "GUI-10",
    ):
        v[g] = ("N/A", "no GUI (non-GUI library/CLI).")
    ob = "no owned observability surface — consumes `mgf.common.observability` (OB rules bind the owner)."
    for o in (
        "OB-01",
        "OB-02",
        "OB-03",
        "OB-04",
        "OB-05",
        "OB-07",
        "OB-08",
        "OB-09",
        "OB-10",
        "OB-11",
    ):
        v[o] = ("N/A", ob)
    if qt == 0:
        v["GUI-01"] = ("✅", "imports no Qt at module level (GUI-01).")
    else:
        v["GUI-01"] = (
            "⏳",
            f"FLAG: {qt} src file(s) reference Qt — verify it is not a module-level import.",
        )
        flags.append("GUI-01 (Qt referenced)")
    if dt:
        v["TM-01"] = (
            "✅",
            "UTC at boundaries — tz-aware `datetime`/UTC (consistent with the repo's DP-12 ✅).",
        )
        v["TM-02"] = ("✅", "ISO-8601/RFC-3339 wire format for datetimes.")
        v["TM-04"] = (
            "✅",
            "no untestable time-dependent logic (timestamps are metadata / injectable).",
        )
    else:
        v["TM-01"] = ("N/A", "no datetime crosses a boundary in this repo's code.")
        v["TM-02"] = ("N/A", "no datetime wire format emitted.")
        v["TM-04"] = ("N/A", "no `datetime.now()` in production logic.")
    v["TM-08"] = ("✅", "async sleeps use `asyncio.sleep`.") if asy else ("N/A", "no async.")
    if not perf:
        for p in ("PF-01", "PF-02", "PF-03", "PF-04", "PF-05", "PF-07", "PF-08"):
            v[p] = ("N/A", "no performance budgets / perf tests.")
    else:
        v["PF-02"] = ("✅", "perf tests marked `@pytest.mark.perf`.")
        v["PF-07"] = ("✅", "perf failures surface the comparison numerically.")
        v["PF-04"] = ("N/A", "re-baselining flow activates with recorded baselines.")
        v["PF-08"] = ("N/A", "thresholds are relative-drop, not hardware-tier-sensitive.")
        ok = baselines
        v["PF-01"] = (
            "✅" if ok else "⏳",
            "regression threshold vs recorded baseline."
            if ok
            else "FLAG: in-file thresholds, no recorded baseline.",
        )
        v["PF-03"] = (
            "✅" if ok else "⏳",
            "baselines in `tests/perf/baselines.json`."
            if ok
            else "FLAG: no `tests/perf/baselines.json`.",
        )
        v["PF-05"] = (
            "✅" if ok else "⏳",
            "warm-up + sample harness."
            if ok
            else "FLAG: plain-pytest timing, no statistical harness.",
        )
        flags.append(f"PF (perf tests {'+baselines' if baselines else 'no baselines'})")
    return v, flags


def apply(repo: Path) -> str:
    led = repo / "docs" / "inprogress" / "MGF_STANDARDS_CONFORMANCE.md"
    if not led.is_file():
        return f"{repo.name}: NO LEDGER"
    text = led.read_text(encoding="utf-8")
    present = set(re.findall(r"^\| ([A-Z]{2,5}-\d+) \|", text, flags=re.M))
    v, flags = verdicts(repo)
    to_add = [rid for rid in NEW_IDS if rid not in present]
    if not to_add:
        return f"{repo.name}: already has all 40 new ids (skip)"
    rows = [f"| {rid} | {v[rid][0]} | {v[rid][1]} |" for rid in to_add]
    lines = text.splitlines()
    last = max(i for i, ln in enumerate(lines) if re.match(r"^\| [A-Z]{2,5}-\d+ \|", ln))
    lines[last + 1 : last + 1] = rows
    out = "\n".join(lines) + "\n"
    out = out.replace("all 218 current MUSTs", "all 258 current MUSTs (PAPER-100 re-baseline)")
    out = out.replace("all 218 MUSTs", "all 258 MUSTs (PAPER-100 re-baseline)")
    led.write_text(out, encoding="utf-8")
    ids = len(set(re.findall(r"\| ([A-Z]{2,5}-\d+) \|", out)))
    ok, wip = out.count("| ✅ |"), out.count("| ⏳ |")
    f = ("  FLAGS: " + "; ".join(flags)) if flags else ""
    return f"{repo.name}: +{len(to_add)} → {ids} ids, {ok}✅/{wip}⏳, conf {round(100 * ok / (ok + wip))}%{f}"


if __name__ == "__main__":
    root = _federation_root()
    for name in sys.argv[1:]:
        print(apply(root / name))
