#!/usr/bin/env python3
import hashlib, hmac, json, sys


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

def digest(obj):
    return hashlib.sha256(canon(obj)).hexdigest()

def sign(key, payload):
    return hmac.new(key.encode(), canon(payload), hashlib.sha256).hexdigest()

def verify(packet, owner_keys, receipts):
    required = {"capture_id", "nonce", "capture_at", "contract_version", "payload", "owner_id", "signature"}
    missing = sorted(required - packet.keys())
    if missing:
        return {"integrity": "UNKNOWN", "occasion": "UNKNOWN", "reason": "missing_fields:" + ",".join(missing)}
    unsigned = {k: packet[k] for k in packet if k != "signature"}
    key = owner_keys.get(packet["owner_id"])
    if key is None:
        return {"integrity": "UNKNOWN", "occasion": "UNKNOWN", "reason": "unknown_owner"}
    good_sig = hmac.compare_digest(packet["signature"], sign(key, unsigned))
    integrity = "PASS" if good_sig else "FAIL"
    receipt = receipts.get(packet["capture_id"])
    occasion = "PASS" if (good_sig and receipt and receipt.get("envelope_digest") == digest(unsigned)
                            and receipt.get("nonce") == packet["nonce"]
                            and receipt.get("owner_id") == packet["owner_id"]
                            and receipt.get("issued_at")) else "UNKNOWN"
    reason = "owner_signature_exact_bytes" if good_sig else "signature_mismatch"
    if good_sig and occasion == "PASS": reason += "+independent_issuance_receipt"
    return {"integrity": integrity, "occasion": occasion, "reason": reason}

def make_packet(key="owner-secret", payload=None, **kw):
    p = {"capture_id":"cap-001", "nonce":"n-001", "capture_at":"2026-09-14T08:00:00Z", "contract_version":"continuity.v1", "payload":payload or {"tree_size":3,"sealed_entries":3}, "owner_id":"owner-A"}
    p.update(kw)
    p["signature"] = sign(key, p)
    return p

def main():
    key = "owner-secret"
    base = make_packet(key)
    receipt = {"envelope_digest": digest({k:base[k] for k in base if k != "signature"}), "nonce":"n-001", "owner_id":"owner-A", "issued_at":"2026-09-14T08:00:00.100Z"}
    fixtures = {
      "A_owner_packet_with_receipt": (base, {"cap-001":receipt}),
      "B_identical_copy_no_receipt": (json.loads(json.dumps(base)), {}),
      "C_mutated_after_signature": ({**base, "payload":{"tree_size":4,"sealed_entries":3}}, {"cap-001":receipt}),
      "D_later_countersignature_reconstructed": (make_packet(key, payload={"tree_size":3,"sealed_entries":3}, capture_at="2026-09-14T08:05:00Z", nonce="n-later"), {})
    }
    out = {name: verify(packet, {"owner-A":key}, receipts) for name,(packet,receipts) in fixtures.items()}
    expected = {
      "A_owner_packet_with_receipt": ("PASS","PASS"),
      "B_identical_copy_no_receipt": ("PASS","UNKNOWN"),
      "C_mutated_after_signature": ("FAIL","UNKNOWN"),
      "D_later_countersignature_reconstructed": ("PASS","UNKNOWN")
    }
    for name, result in out.items():
        assert (result["integrity"], result["occasion"]) == expected[name], (name, result)
    print(json.dumps({"status":"PASS", "cases":out}, indent=2, sort_keys=True))

if __name__ == "__main__": main()
