"""Strict offline verifier for an exported Erdős public receipt bundle.

Usage from an extracted bundle:

    python verify_release.py receipt.json --strict

No network and no third-party package are required.  The command ignores the
receipt's cached PASS booleans, rechecks the full witness with the bundled exact
determinant implementations, audits the bundled SQLite Ledger, and verifies the
SHA-256 manifest before printing PASS.
"""

from __future__ import annotations

import argparse
import hashlib
import json
from pathlib import Path

import replay
from release_contract import ReleaseContractError, require_pass, verify_published_witness


class BundleVerificationError(ValueError):
    pass


def sha256_file(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as handle:
        for chunk in iter(lambda: handle.read(1024 * 1024), b""):
            digest.update(chunk)
    return digest.hexdigest()


def load_json(path: Path):
    with path.open(encoding="utf-8") as handle:
        return json.load(handle)


def verify_bundle(receipt_path: str | Path, *, strict: bool = True) -> dict:
    receipt_path = Path(receipt_path).resolve()
    root = receipt_path.parent
    receipt = load_json(receipt_path)
    if receipt.get("schema_version") != "erdos.verification-receipt.v1":
        raise BundleVerificationError("unsupported or missing receipt schema_version")

    manifest = load_json(root / "manifest.json")
    if manifest.get("schema_version") != "erdos.release-manifest.v1":
        raise BundleVerificationError("unsupported or missing manifest schema_version")
    for item in manifest.get("files", []):
        path = root / item["path"]
        if not path.is_file():
            raise BundleVerificationError(f"manifest file missing: {item['path']}")
        actual = sha256_file(path)
        if actual != item["sha256"]:
            raise BundleVerificationError(f"manifest digest mismatch: {item['path']}")

    classification = receipt.get("classification", {})
    required_false = ("novelty_claimed", "optimality_claimed", "discovered_by_erdos")
    if any(classification.get(name) is not False for name in required_false):
        raise BundleVerificationError("this receipt must not claim novelty, optimality, or discovery")
    if classification.get("mode") != "published_witness_reverification":
        raise BundleVerificationError("unexpected receipt classification mode")

    source_witness = load_json(root / receipt["witness"]["artifact"])
    try:
        measured = require_pass(verify_published_witness(
            source_witness["published_points_0indexed"],
            source_witness["erdos_frame_points_1indexed"],
        ))
    except ReleaseContractError as exc:
        raise BundleVerificationError(str(exc)) from exc

    recorded = receipt.get("verification", {})
    for key in (
        "status", "point_count", "unique_count", "subsets_expected",
        "subsets_checked", "verifier_disagreements", "witness_sha256",
    ):
        if recorded.get(key) != measured.get(key):
            raise BundleVerificationError(
                f"receipt verification field {key!r} does not match measured evidence"
            )
    if measured["witness_sha256"] != receipt["witness"].get("canonical_sha256"):
        raise BundleVerificationError("witness digest mismatch")

    ledger_path = root / receipt["provenance"]["ledger_sqlite"]["path"]
    audit = replay.audit(str(ledger_path))
    if not audit["consistent"]:
        raise BundleVerificationError(f"Ledger replay inconsistent: {audit['violations']}")
    if audit["run_id"] != receipt["provenance"].get("run_id"):
        raise BundleVerificationError("receipt run_id does not match the bundled Ledger")
    if audit.get("independent_witness_rechecks") != 1:
        raise BundleVerificationError("Ledger audit did not independently recheck the witness")

    ledger_export = load_json(root / receipt["provenance"]["ledger_export"]["path"])
    if ledger_export.get("run") != audit["run"] or ledger_export.get("nodes") != audit["nodes"]:
        raise BundleVerificationError("JSON Ledger export does not match the SQLite Ledger")

    if strict:
        source_path = root / receipt["source"]["artifact"]
        if sha256_file(source_path) != receipt["source"]["sha256"]:
            raise BundleVerificationError("pinned source notebook digest mismatch")
        for name, expected in receipt["provenance"].get("code_sha256", {}).items():
            if sha256_file(root / name) != expected:
                raise BundleVerificationError(f"bundled verifier code mismatch: {name}")

    return {
        "status": "pass",
        "release_id": receipt["release_id"],
        "claim": receipt["claim"]["notation"],
        "claim_scope": receipt["claim"]["scope"],
        "points": measured["point_count"],
        "subsets_checked": measured["subsets_checked"],
        "subsets_expected": measured["subsets_expected"],
        "verifiers": [item["id"] for item in measured["verifiers"]],
        "replay": "consistent",
        "novelty_claimed": False,
        "optimality_claimed": False,
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="Verify an Erdős public receipt bundle")
    parser.add_argument("receipt", nargs="?", default="receipt.json")
    parser.add_argument("--strict", action="store_true", help="also verify source and code hashes")
    args = parser.parse_args()
    try:
        result = verify_bundle(args.receipt, strict=args.strict)
    except Exception as exc:
        print(f"FAIL — {exc}")
        return 1

    print("PASS — published witness reverified")
    print(f"Claim scope: {result['claim']} only ({result['claim_scope']})")
    print(f"Points: {result['points']} unique in {{1..8}}^3")
    print(f"Subsets: {result['subsets_checked']} / {result['subsets_expected']}")
    print("Exact verifiers: Bareiss + cofactor + permutation sum — PASS")
    print("Ledger replay: CONSISTENT")
    print("Novelty: false")
    print("Optimality: false")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
