"""The three cross-language digests. `CONFORMANCE.txt` commits them; CI gates them.

  (a) world     SHA-256 over the canonical JSON world for seeds 0–4095,
                reference minutes included. The browser must show the same
                alert for ?seed= as Python generated.
  (b) scorer    SHA-256 over the exact-fraction scores of a fixed grid of
                synthetic episodes: label × disposition × cites × planted ×
                minutes × reference. No floats are hashed.
  (c) protocol  SHA-256 over what parse + engine make of a committed corpus of
                reply strings (`conformance/replies.json`) on fixed worlds:
                accepted actions, rejection reasons and the minute meter after
                every turn. This is the digest wordle does not need and this
                environment cannot do without — JSON-in-prose with fences and a
                brace scan is where two runtimes disagree.

Run:  uv run python -m alert_triage.conformance [--write]
"""

from __future__ import annotations

import hashlib
import json
import sys
from pathlib import Path

from .engine import Engine
from .generator import canonical_json, world_digest, world_for_seed
from .reward import Episode, score_exact

ROOT = Path(__file__).parent.parent
CORPUS = ROOT / "conformance" / "replies.json"
COMMITTED = ROOT / "CONFORMANCE.txt"

GRID_LABELS = ("benign", "suspicious")
GRID_DISPOSITIONS = ("close", "escalate", "none", "truncated")
GRID_CITES = (["a"], ["a", "b"], ["a", "b", "c", "d"], ["x"], ["a", "x"], ["a", "b", "x", "y", "z"], ["a", "a", "b"], ["kyc.z"])
GRID_PLANTED = ([["a"]], [["a", "b"]], [["a", "b"], ["a"]], [["c"], ["a", "b", "c", "d"]])
GRID_MINUTES = (33, 42, 60, 96, 150, 204)
GRID_REFERENCE = (33, 42, 96, None)


def scorer_digest() -> str:
    digest = hashlib.sha256()
    for label in GRID_LABELS:
        for disposition in GRID_DISPOSITIONS:
            for cites in GRID_CITES:
                for planted in GRID_PLANTED:
                    for minutes in GRID_MINUTES:
                        for reference in GRID_REFERENCE:
                            ep = Episode(
                                label=label,
                                planted=planted,
                                disposition=None if disposition in ("none", "truncated") else disposition,
                                cites=[] if disposition in ("none", "truncated") else cites,
                                minutes_spent=minutes,
                                reference_minutes=reference,
                                turns_spent=1,
                                rejected=0,
                                truncated=disposition == "truncated",
                            )
                            digest.update(canonical_json(score_exact(ep)).encode())
                            digest.update(b"\n")
    return digest.hexdigest()


def protocol_trace(corpus: list[dict] | None = None) -> list[dict]:
    corpus = corpus if corpus is not None else json.loads(CORPUS.read_text())
    out = []
    for case in corpus:
        engine = Engine(world_for_seed(case["seed"]))
        turns = []
        for reply in case["replies"]:
            if engine.done:
                break
            step = engine.step(reply)
            turns.append({"accepted": step.accepted, "action": step.action, "reason": step.reason, "minutes": step.minutes_spent, "turns": step.turns_spent})
        ep = engine.episode()
        out.append({"seed": case["seed"], "turns": turns, "outcome": engine.outcome, "truncated": ep.truncated, "score": score_exact(ep)})
    return out


def protocol_digest() -> str:
    digest = hashlib.sha256()
    for entry in protocol_trace():
        digest.update(canonical_json(entry).encode())
        digest.update(b"\n")
    return digest.hexdigest()


def all_digests() -> dict[str, str]:
    return {"world": world_digest(), "scorer": scorer_digest(), "protocol": protocol_digest()}


def committed() -> dict[str, str]:
    out = {}
    for line in COMMITTED.read_text().splitlines():
        parts = line.split()
        if len(parts) >= 2:
            out[parts[1]] = parts[0]
    return out


def main(argv: list[str]) -> int:
    digests = all_digests()
    lines = [
        f"{digests['world']}  world     sha256 over canonical world JSON, seeds 0-4095, newline-joined, reference_minutes included",
        f"{digests['scorer']}  scorer    sha256 over score_exact() of the fixed grid in conformance.py, newline-joined",
        f"{digests['protocol']}  protocol  sha256 over protocol_trace() of conformance/replies.json, newline-joined",
    ]
    if "--write" in argv:
        COMMITTED.write_text("\n".join(lines) + "\n")
        print(f"wrote {COMMITTED}")
    for line in lines:
        print(line)
    if COMMITTED.exists():
        mismatched = [k for k, v in committed().items() if digests.get(k) != v]
        if mismatched:
            print(f"MISMATCH against CONFORMANCE.txt: {mismatched}")
            return 1
    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))
