#!/usr/bin/env python3
"""
Guide coverage reconciler — diffs the CODE surface against what the guide documents,
so nothing new (a service, an SPA module, an agent module) stays undocumented.

Run:  python3 hiveops-guide/scripts/coverage-reconcile.py
Exits 1 if any gaps are found (handy for a session-start check / hook).
"""
import os, re, sys, glob

ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))  # /source/hiveops-src
CONTENT = os.path.join(ROOT, "hiveops-guide", "backend", "src", "main", "resources", "content")

# product SPAs that should have per-module role-tab guides (app prefix in the guide)
PRODUCT_APPS = {
    "analytics","aria","claims","dashboard","devices","fleet","incident","msp",
    "profile","recon","reports","transactions","vault",
}
# guide app-prefixes that are NOT code-derived (concept / process docs) — skip in the code diff
NON_CODE_APPS = {"platform","devops","technical","processes"}
# the 5 role-tabs expected on a product module
PRODUCT_TABS = {"Customer","Internal","Architect","Testing","Claude"}
# dir-name (minus hiveops-) → guide app/service key, where they differ
SERVICE_ALIAS = {"ai":"ai", "recon":"recon"}  # extend as needed


def load_guide():
    """module_key -> set(tab names). Reads frontmatter of every content/**/*.md."""
    mods = {}
    for f in glob.glob(os.path.join(CONTENT, "**", "*.md"), recursive=True):
        txt = open(f, encoding="utf-8").read()
        m = re.match(r"^---\n(.*?)\n---", txt, re.S)
        if not m:
            continue
        fm = dict(re.findall(r"^(\w+):\s*(.+)$", m.group(1), re.M))
        key, tab = fm.get("module"), fm.get("tab")
        if key:
            mods.setdefault(key, set()).add(tab)
    return mods


def has_backend(repo):
    return bool(glob.glob(os.path.join(repo, "**", "src", "main", "java", "com", "hiveops"),
                          recursive=True))


def main():
    guide = load_guide()
    apps = {k.split(".")[0] for k in guide}
    gaps = []

    # 1) backend services → expect a technical.<svc> guide
    for repo in sorted(glob.glob(os.path.join(ROOT, "hiveops-*"))):
        name = os.path.basename(repo)
        svc = name[len("hiveops-"):]
        # non-service / special repos, and DEPRECATED ones (qds-monitor + smartjournal were the
        # early incident bridge before the agent shipped — superseded, don't document)
        if svc in ("agent","guide","bom","security-common","template","docs","release",
                   "devops","integration","tools","adoons",
                   "qds-monitor","smartjournal"):  # last line = deprecated
            continue
        if not os.path.isdir(repo) or not has_backend(repo):
            continue
        if f"technical.{svc}" not in guide:
            gaps.append(("SERVICE",  f"hiveops-{svc}", f"technical.{svc}"))

    # 2) agent modules → expect an agent.<module> guide
    for mod in sorted(glob.glob(os.path.join(ROOT, "hiveops-agent", "hiveops-module-*"))):
        if not os.path.isdir(mod):
            continue
        slug = os.path.basename(mod)[len("hiveops-module-"):]
        if f"agent.{slug}" not in guide:
            gaps.append(("AGENT MODULE", os.path.basename(mod), f"agent.{slug}"))

    # 3) product modules present but missing role-tabs
    incomplete = []
    for key, tabs in sorted(guide.items()):
        app = key.split(".")[0]
        if app in PRODUCT_APPS:
            missing = PRODUCT_TABS - tabs
            if missing:
                incomplete.append((key, ",".join(sorted(missing))))

    # 4) product SPAs with no guide content at all
    for repo in sorted(glob.glob(os.path.join(ROOT, "hiveops-*"))):
        app = os.path.basename(repo)[len("hiveops-"):]
        if app in PRODUCT_APPS and app not in apps and os.path.isdir(os.path.join(repo, "frontend")):
            gaps.append(("SPA", f"hiveops-{app}", f"{app}.* (no modules documented)"))

    # ---- report ----
    print(f"Guide coverage — {len(guide)} documented modules across {len(apps)} apps\n")
    if gaps:
        print(f"❌ {len(gaps)} MISSING (in code, not in guide):")
        for kind, code, expected in gaps:
            print(f"   [{kind}] {code}  →  needs  {expected}")
    else:
        print("✅ no missing services / agent modules / SPAs")
    if incomplete:
        print(f"\n⚠️  {len(incomplete)} product modules missing role-tabs:")
        for key, miss in incomplete:
            print(f"   {key}  →  missing: {miss}")
    print()
    sys.exit(1 if (gaps or incomplete) else 0)


if __name__ == "__main__":
    main()
