#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""check_stands.py — the gate on documentation/architecture/where-felhom-stands.yaml.

The page that dataset renders is the operator's picture of where the product stands. Its whole
value is that every claim on it is traceable, so this checks the traceability rather than the
claims: a claim nobody can trace is not a weaker claim, it is not a claim at all.

WHAT IT CONVICTS ON (each is a FAIL, exit 1):

  1. an entry with NO source                    — §4 rule 1: that is a defect, not a claim
  2. an `evidence:` path that does not resolve  — a citation nobody opened
  3. a `register:` id absent from OPEN-ITEMS.md — a dangling register reference
  4. a `capability-map:` anchor not found       — the row it derives from has moved or gone
  5. status `walked` with no `evidence:` source — THE LOAD-BEARING ONE. "Walked" means done end
     to end on real hardware with evidence on file. If no evidence document is cited, the page is
     drawing a green dot from an opinion. This is the rule the positive control exercises: take a
     claim the page marks `missing`, mark it `walked`, and this fires.

WHAT IT DOES NOT DO, said plainly so the green is not over-read: it does not read the evidence and
judge whether it supports the claim, and it cannot. A human verdict lives in each entry's
`verified:` block; this gate checks that the paperwork exists, not that the paperwork is right.

Run:  python3 scripts/check_stands.py [path-to-yaml]
"""
import os
import re
import sys

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DEFAULT = os.path.join(ROOT, "documentation", "architecture", "where-felhom-stands.yaml")
REGISTER = os.path.join(ROOT, "documentation", "backlog", "OPEN-ITEMS.md")
CAPMAP = os.path.join(ROOT, "documentation", "architecture", "00-capability-map.md")
DOCS = os.path.join(ROOT, "documentation")


def load(path):
    """Minimal parser for the shape this file is written in.

    Deliberately not PyYAML: the runner image and the gate hosts carry python3 and nothing else
    (the same constraint that keeps `uses:` out of the CI workflow), and a gate that needs a pip
    install is a gate that silently stops running.
    """
    claims, cur, in_sources = [], None, False
    for raw in open(path, encoding="utf-8"):
        line = raw.rstrip("\n")
        if line.startswith("  - id:"):
            cur = {"id": line.split(":", 1)[1].strip(), "sources": []}
            claims.append(cur)
            in_sources = False
            continue
        if cur is None:
            continue
        if line.strip() == "sources:":
            in_sources = True
            continue
        m = re.match(r'\s+- (capability-map|evidence|register): (.*)$', line)
        if in_sources and m:
            cur["sources"].append((m.group(1), m.group(2).strip().strip('"')))
            continue
        m = re.match(r'\s+(status|title|verdict|depth): (.*)$', line)
        if m:
            in_sources = False
            cur[m.group(1)] = m.group(2).strip().strip('"')
    return claims


def main():
    path = sys.argv[1] if len(sys.argv) > 1 else DEFAULT
    claims = load(path)
    register = open(REGISTER, encoding="utf-8").read()
    # Normalise the map before matching: its row labels carry ** and ` markup, so a literal
    # substring probe against the raw file fails on text that is plainly there. Matching the
    # rendered words is what the citation means.
    capmap = open(CAPMAP, encoding="utf-8").read()
    capmap = re.sub(r"[`*]", "", capmap)
    capmap = re.sub(r"\s+", " ", capmap).lower()

    print("check_stands — %d claim(s) in %s" % (len(claims), os.path.relpath(path, ROOT)))
    fails = []

    for c in claims:
        cid = c["id"]
        if not c["sources"]:
            fails.append("%s: NO SOURCE — an entry with no source is a defect, not a claim" % cid)
        for kind, ref in c["sources"]:
            if kind == "evidence":
                if not os.path.exists(os.path.join(DOCS, ref)):
                    fails.append("%s: evidence path does not resolve: documentation/%s" % (cid, ref))
            elif kind == "register":
                if not re.search(r"\*\*%s\*\*" % re.escape(ref), register):
                    fails.append("%s: register id %s is not in OPEN-ITEMS.md" % (cid, ref))
            elif kind == "capability-map":
                probe = " ".join(ref.split()[:4])
                probe = re.sub(r"[`*]", "", probe)
                if probe and probe.lower() not in capmap:
                    fails.append("%s: capability-map anchor not found: %r" % (cid, probe))
        if c.get("status") == "walked" and not any(k == "evidence" for k, _ in c["sources"]):
            fails.append("%s: status 'walked' but NO evidence document cited — a green dot "
                         "drawn from an opinion" % cid)

    counts = {}
    for c in claims:
        counts[c.get("status")] = counts.get(c.get("status"), 0) + 1
    print("  statuses: " + ", ".join("%s=%d" % kv for kv in sorted(counts.items())))

    if fails:
        print("\nCONVICTED — %d problem(s):" % len(fails))
        for f in fails:
            print("  " + f)
        return 1
    print("\ncheck_stands: OK — every claim cites a source, every citation resolves, and every "
          "'walked' cites a walk.")
    return 0


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