"""verifyctl — evidence-archive verifier for Wannabe on-chain evidence.

Built as the deliverable of arena task genesis-verifyctl-001: the hidden test
suite's SHA-256 was committed on XRPL testnet BEFORE this file was written
(commit tx 272A9CB4EEB0EEE9331854002B383FCB6ABCBEAC50DBBE4B7F610824FA5B8739),
and the build was settled through escrow by the deterministic referee.

Checks an archive of XRPL transaction JSONs (as produced in evidence/tx_archive/):
per-file integrity against MANIFEST.sha256, filename==tx hash, tesSUCCESS,
validated. Stdlib only, fully offline.

Usage: python verifyctl.py <archive_dir>
Exit codes: 0 = archive clean, 2 = problems found.
"""
import hashlib
import json
import os
import re
import sys

_HASH_NAME = re.compile(r"^[0-9A-Fa-f]{64}\.json$")


def _tx_fields(obj):
    """Return (hash, transaction_result, validated) from a tx JSON of either
    shape: {"result": {...}} or the bare result object."""
    res = obj.get("result", obj) if isinstance(obj, dict) else {}
    if not isinstance(res, dict):
        res = {}
    tx_hash = res.get("hash") or (obj.get("hash") if isinstance(obj, dict) else None)
    meta = res.get("meta") or (obj.get("meta") if isinstance(obj, dict) else None) or {}
    result_code = meta.get("TransactionResult") if isinstance(meta, dict) else None
    validated = res.get("validated")
    if validated is None and isinstance(obj, dict):
        validated = obj.get("validated")
    return tx_hash, result_code, validated


def _read_manifest(path):
    """Parse MANIFEST.sha256 -> {filename: sha256}. Lines: '<sha256>  <name>'."""
    entries = {}
    with open(path) as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            parts = line.split(None, 1)
            if len(parts) == 2:
                digest, name = parts
                entries[name.strip()] = digest.lower()
    return entries


def verify(archive_dir):
    problems = []
    json_files = sorted(
        fn for fn in os.listdir(archive_dir) if fn.lower().endswith(".json")
    )
    per_file_problems = {fn: [] for fn in json_files}

    manifest_path = os.path.join(archive_dir, "MANIFEST.sha256")
    manifest = None
    if os.path.exists(manifest_path):
        manifest = _read_manifest(manifest_path)
    else:
        problems.append({"file": "MANIFEST.sha256", "error": "manifest_missing"})

    for fn in json_files:
        path = os.path.join(archive_dir, fn)
        raw = open(path, "rb").read()

        if manifest is None:
            per_file_problems[fn].append("unlisted")
        elif fn not in manifest:
            per_file_problems[fn].append("unlisted")
        elif hashlib.sha256(raw).hexdigest() != manifest[fn]:
            per_file_problems[fn].append("sha256_mismatch")

        try:
            obj = json.loads(raw.decode("utf-8"))
        except (ValueError, UnicodeDecodeError):
            per_file_problems[fn].append("bad_json")
            continue

        tx_hash, result_code, validated = _tx_fields(obj)
        stem = fn[:-5]
        if _HASH_NAME.match(fn):
            if not tx_hash or tx_hash.upper() != stem.upper():
                per_file_problems[fn].append("hash_mismatch")
        if result_code != "tesSUCCESS":
            per_file_problems[fn].append("not_success")
        if validated is not True:
            per_file_problems[fn].append("not_validated")

    if manifest is not None:
        for name in sorted(manifest):
            if name.lower().endswith(".json") and name not in per_file_problems:
                problems.append({"file": name, "error": "listed_missing"})

    passed = failed = 0
    for fn in json_files:
        errs = per_file_problems[fn]
        if errs:
            failed += 1
            problems.extend({"file": fn, "error": e} for e in errs)
        else:
            passed += 1

    return {
        "checked": len(json_files),
        "passed": passed,
        "failed": failed,
        "problems": problems,
    }


def main(argv):
    if len(argv) != 2:
        print(json.dumps({"error": "usage: verifyctl.py <archive_dir>"}))
        return 2
    report = verify(argv[1])
    print(json.dumps(report, sort_keys=True, indent=1))
    return 0 if report["failed"] == 0 else 2


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