"""Bounded, exact C4/C6 method runner. Python standard library; no network.

Run from a sealed standalone bundle. A receipt records computation, not proof
acceptance. Resume checks the earlier trace before extending its declared cap.
"""
import argparse
import hashlib
import json
import re
import sys
from fractions import Fraction as F
from pathlib import Path

ROOT = Path(__file__).resolve().parent
MAX_BYTES = 262144


def canon(value):
    return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode()


def digest(data):
    return "sha256:" + hashlib.sha256(data).hexdigest()


def pairs(items):
    result = {}
    for key, value in items:
        if key in result:
            raise ValueError("DUPLICATE_JSON_KEY")
        result[key] = value
    return result


def read_json(path):
    with Path(path).open("rb") as handle:
        raw = handle.read(MAX_BYTES + 1)
    if len(raw) > MAX_BYTES:
        raise ValueError("INPUT_BYTES_EXCEEDED")
    return json.loads(raw, object_pairs_hook=pairs,
                      parse_constant=lambda _: (_ for _ in ()).throw(ValueError("INVALID_JSON_NUMBER")))


def bundle():
    manifest = read_json(ROOT / "manifest.json")
    for item in manifest["files"]:
        path = ROOT / item["path"]
        if not path.resolve().is_relative_to(ROOT) or path.is_symlink():
            raise ValueError("BUNDLE_PATH_ESCAPE")
        raw = path.read_bytes()
        if len(raw) != item["bytes"] or digest(raw) != item["sha256"]:
            raise ValueError("BUNDLE_FILE_CHANGED: " + item["path"])
    return manifest, digest(canon(manifest))


def rational(value):
    if not isinstance(value, str) or not re.fullmatch(r"(?:0|[1-9][0-9]{0,4})(?:/[1-9][0-9]{0,4})?", value):
        raise ValueError("USE_BOUNDED_RATIONAL_STRINGS")
    result = F(value)
    if result.denominator > 10000 or result.numerator > 10000:
        raise ValueError("RATIONAL_LIMIT")
    return result


def inputs(value):
    if not isinstance(value, dict) or set(value) != {"n", "q", "epsilon", "max_steps"}:
        raise ValueError("EXACT_INPUT_FIELDS_REQUIRED")
    if type(value["n"]) is not int or value["n"] not in (4, 6):
        raise ValueError("ONLY_C4_AND_C6")
    if type(value["max_steps"]) is not int or not 0 <= value["max_steps"] <= 128:
        raise ValueError("STEP_CAP_0_TO_128")
    q, eps = rational(value["q"]), rational(value["epsilon"])
    if not 0 <= q <= 1 or not 0 < eps < 1:
        raise ValueError("PARAMETER_SCOPE")
    return {**value, "q": str(q), "epsilon": str(eps)}


def compute(value):
    value = inputs(value)
    n, q, eps, cap = value["n"], F(value["q"]), F(value["epsilon"]), value["max_steps"]
    p = [F(i == 0) for i in range(n)]
    beta = max(q, abs(2*q-1)) if n == 4 else max((1+q)/2, abs((3*q-1)/2), abs(2*q-1))
    trace, first, certificate = [], None, None
    for t in range(cap + 1):
        distance = sum(abs(x-F(1, n)) for x in p)/2
        trace.append({"step": t, "tv": str(distance)})
        if first is None and distance <= eps:
            first = t
        # C4 retains the older convenient beta^t bound. C6 needs sqrt(5)/2;
        # comparing squares retains exact rational arithmetic and that factor.
        condition = beta**t <= eps if n == 4 else 5*beta**(2*t) <= 4*eps**2
        if certificate is None and condition:
            certificate = t
        if t < cap:
            p = [q*p[j] + (1-q)*(p[(j-1)%n]+p[(j+1)%n])/2 for j in range(n)]
    return {"profile": "wrs-markov-computation-1", "input": value,
            "status": "THRESHOLD_FOUND" if first is not None else "CAP_REACHED",
            "first_threshold_step": first, "last_step": cap, "last_row": list(map(str, p)),
            "tv_at_last_step": trace[-1]["tv"], "trace": trace,
            "spectral_radius": str(beta), "sufficient_certificate_step": certificate,
            "certificate_rule": "beta^t <= epsilon" if n == 4 else "5*beta^(2*t) <= 4*epsilon^2",
            "scope": "Homogeneous holding random walk on C4/C6, uniform target, worst point start by rotation symmetry.",
            "limits": ["CAP_REACHED means no threshold found through this cap; it does not prove no finite mixing time.",
                       "This finite computation neither optimizes over all q nor establishes novelty or independent scientific acceptance.",
                       "A sufficient spectral certificate is not the minimum mixing time."]}


def run(value, previous=None):
    manifest, identity = bundle()
    value = inputs(value)
    prior_hash = None
    if previous is not None:
        if not isinstance(previous, dict) or set(previous) != {"profile", "capability", "source_refs", "computation", "continuation_of"}:
            raise ValueError("INVALID_PRIOR_RECEIPT")
        if previous["profile"] != "wrs-markov-run-1" or previous["capability"] != identity or previous["source_refs"] != manifest["source_refs"]:
            raise ValueError("PRIOR_VERSION_MISMATCH")
        old = previous["computation"]
        if old != compute(old["input"]):
            raise ValueError("PRIOR_COMPUTATION_MISMATCH")
        if any(value[k] != old["input"][k] for k in ("n", "q", "epsilon")) or value["max_steps"] <= old["input"]["max_steps"]:
            raise ValueError("RESUME_MUST_EXTEND_SAME_PROBLEM")
        prior_hash = digest(canon(previous))
    return {"profile": "wrs-markov-run-1", "capability": identity,
            "source_refs": manifest["source_refs"], "computation": compute(value),
            "continuation_of": prior_hash}


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--input", help="Small JSON file: n, q, epsilon, max_steps")
    parser.add_argument("--resume", help="Exact previous receipt; same n/q/epsilon and a larger cap required")
    parser.add_argument("--output", help="New output file; existing files are never overwritten")
    parser.add_argument("--verify", action="store_true", help="Check package hashes and frozen reference cases")
    args = parser.parse_args()
    if args.verify:
        if args.input or args.resume or args.output:
            raise ValueError("VERIFY_IS_A_SEPARATE_OPERATION")
        _, identity = bundle()
        for case in read_json(ROOT / "reference-cases.json"):
            observed = compute(case["input"])
            for key, expected in case["expected"].items():
                if observed[key] != expected:
                    raise ValueError("REFERENCE_CASE_MISMATCH")
        output = {"status": "REFERENCE_CHECKS_PASS", "capability": identity,
                  "scientific_certification": "NONE"}
    else:
        if not args.input:
            raise ValueError("INPUT_REQUIRED")
        output = run(read_json(args.input), read_json(args.resume) if args.resume else None)
    encoded = json.dumps(output, indent=2) + "\n"
    if args.output:
        with Path(args.output).open("x", encoding="utf-8") as handle:
            handle.write(encoded)
    else:
        print(encoded, end="")


if __name__ == "__main__":
    try:
        main()
    except (ValueError, OSError, KeyError, TypeError) as exc:
        print(json.dumps({"status": "REJECTED", "error": str(exc)}), file=sys.stderr)
        sys.exit(2)
