#!/usr/bin/env python3
"""Audit auto-rules for the Rule-Governance pass (Phase 5.1 of
road-to-augment-limit-fit).

Walk `.agent-src.uncondensed/rules/*.md`, collect per-rule frontmatter
(`description`, `triggers`, `routes_to`, `tier`), measure body and
registry-stub costs, and emit:

- `agents/runtime/reports/auto-rules-audit.json` — deterministic, machine-readable.
- `agents/runtime/reports/auto-rules-audit.md`   — ranked summary for review.

The registry-stub cost mirrors `scripts/measure_augment_budget.py`'s
accounting model: only the stub line is injected into the Augment
workspace-guidelines budget for `type: auto` rules. The body cost is
informational — it ships in the projected `.augment/rules/` tree but
does NOT count against the 49,512-char ceiling.
"""

from __future__ import annotations

import json
import sys
from pathlib import Path

import yaml

REPO_ROOT = Path(__file__).resolve().parent.parent.parent
sys.path.insert(0, str(REPO_ROOT / "src" / "scripts"))
from _lib.agent_src import artefact_roots  # noqa: E402

# Pre-monorepo this was REPO_ROOT/.agent-src.uncondensed/rules. Post-move
# (ADR-017) source rules live under packages/*/.agent-src.uncondensed/rules.
def _src_rule_paths() -> list[Path]:
    paths: list[Path] = []
    seen: set[str] = set()
    for root in artefact_roots():
        d = root / "rules"
        if d.is_dir():
            for p in sorted(d.glob("*.md")):
                if p.name not in seen:
                    seen.add(p.name)
                    paths.append(p)
    return paths

PROJECTED_RULES = REPO_ROOT / "dist/agent-src" / "rules"
REPORT_DIR = REPO_ROOT / "agents" / "reports"
JSON_OUT = REPORT_DIR / "auto-rules-audit.json"
MD_OUT = REPORT_DIR / "auto-rules-audit.md"

# Stub Augment injects per auto-rule. Mirrors measure_augment_budget.STUB_TEMPLATE.
STUB_TEMPLATE = (
    'If the user prompt matches the description "{desc}", '
    "read the file located in {path}"
)


def _split_frontmatter(text: str) -> tuple[dict, str]:
    if not text.startswith("---\n"):
        return {}, text
    end = text.find("\n---", 4)
    if end < 0:
        return {}, text
    fm = yaml.safe_load(text[4:end]) or {}
    body = text[end + 4 :].lstrip("\n")
    return fm, body


def _trigger_summary(triggers: list) -> dict:
    paths: list[str] = []
    keywords: list[str] = []
    intents: list[str] = []
    for entry in triggers or []:
        if not isinstance(entry, dict):
            continue
        if "path_prefix" in entry:
            paths.append(str(entry["path_prefix"]))
        if "keyword" in entry:
            keywords.append(str(entry["keyword"]))
        if "intent" in entry:
            intents.append(str(entry["intent"]))
    return {"path_prefixes": paths, "keywords": keywords, "intents": intents}


def collect() -> list[dict]:
    rules: list[dict] = []
    for path in _src_rule_paths():
        text = path.read_text(encoding="utf-8")
        fm, body = _split_frontmatter(text)
        if fm.get("type") != "auto":
            continue
        desc = (fm.get("description") or "").strip()
        rel_projected = f".augment/rules/{path.name}"
        stub = STUB_TEMPLATE.format(desc=desc, path=rel_projected)
        triggers = _trigger_summary(fm.get("triggers") or [])
        routes_to = list(fm.get("routes_to") or [])
        rules.append(
            {
                "name": path.stem,
                "src_path": str(path.relative_to(REPO_ROOT)),
                "tier": fm.get("tier"),
                "description": desc,
                "description_chars": len(desc),
                "triggers": triggers,
                "trigger_count": (
                    len(triggers["path_prefixes"])
                    + len(triggers["keywords"])
                    + len(triggers["intents"])
                ),
                "routes_to": routes_to,
                "body_chars": len(body),
                "file_chars": len(text),
                "stub_chars": len(stub),
            }
        )
    return rules


def render_markdown(rules: list[dict]) -> str:
    total_stub = sum(r["stub_chars"] for r in rules)
    total_body = sum(r["body_chars"] for r in rules)
    total_desc = sum(r["description_chars"] for r in rules)
    lines = [
        "# Auto-Rule Audit",
        "",
        "Generated by `scripts/audit_auto_rules.py` for Phase 5 of",
        "`agents/roadmaps/archive/road-to-augment-limit-fit.md`. Re-run after",
        "any rule add/merge/deprecate to refresh the baseline.",
        "",
        "## Totals",
        "",
        f"- auto-rules: **{len(rules)}**",
        f"- registry-stub cost (counts against 49,512 cap): **{total_stub:,}** chars",
        f"- description chars (subset of stub cost): **{total_desc:,}** chars",
        f"- body chars (informational, NOT in budget): **{total_body:,}** chars",
        "",
        "## Ranked by registry-stub cost",
        "",
        "| # | Rule | Tier | Desc | Stub | Body | Triggers | Routes |",
        "|---|------|------|------|------|------|----------|--------|",
    ]
    for i, r in enumerate(sorted(rules, key=lambda x: -x["stub_chars"]), 1):
        triggers = (
            f"{len(r['triggers']['path_prefixes'])}p / "
            f"{len(r['triggers']['keywords'])}k / "
            f"{len(r['triggers']['intents'])}i"
        )
        routes = ", ".join(r["routes_to"]) or "—"
        lines.append(
            f"| {i} | `{r['name']}` | {r['tier'] or '—'} | "
            f"{r['description_chars']} | {r['stub_chars']} | "
            f"{r['body_chars']} | {triggers} | {routes} |"
        )
    lines.append("")
    lines.append("Trigger key: `Np` = path-prefix, `Nk` = keyword, `Ni` = intent.")
    lines.append("")
    return "\n".join(lines)


def main() -> int:
    if not _src_rule_paths():
        print("❌  No source rules found under any artefact root's rules/", file=sys.stderr)
        return 1
    rules = collect()
    REPORT_DIR.mkdir(parents=True, exist_ok=True)
    payload = {"rule_count": len(rules), "rules": rules}
    JSON_OUT.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
    MD_OUT.write_text(render_markdown(rules), encoding="utf-8")
    print(f"✅  Audited {len(rules)} auto-rules.")
    print(f"   JSON: {JSON_OUT.relative_to(REPO_ROOT)}")
    print(f"   MD:   {MD_OUT.relative_to(REPO_ROOT)}")
    return 0


if __name__ == "__main__":
    sys.exit(main())
