#!/usr/bin/env python3
"""receipt.py — ACR-1 Agent Claim Receipt: issue / verify / countersign.

A receipt binds a signed claim (e.g. "these commit SHAs are my work") to an
Ed25519 key whose fingerprint is embedded and self-verified. A counterparty
(the platform, buyer, or funder) may countersign the same document, turning a
self-assertion into a two-party attestation. Receipts may anchor prior receipt
hashes to form an evidentiary chain.

Stdlib only. See SPEC.md for the format.
"""
import hashlib
import json
import os
import time

import ed25519

SCHEMA = "acr-1"


def canonical(obj):
    """ACR-1 canonical form: sorted keys, tight separators, ASCII-safe UTF-8.
    Deterministic and recomputable; it is NOT RFC 8785 JCS (see SPEC.md)."""
    return json.dumps(obj, sort_keys=True, separators=(",", ":"),
                      ensure_ascii=True).encode("utf-8")


def generate_seed():
    return os.urandom(32)


def key_id(pk_hex):
    """Fingerprint = first 8 bytes of sha256(pubkey), hex."""
    return hashlib.sha256(bytes.fromhex(pk_hex)).hexdigest()[:16]


def _signer_block(name, pk_hex):
    return {"name": name, "key_id": key_id(pk_hex), "pubkey": pk_hex}


def issue(issuer_name, seed_hex, subject_kind, refs, claim, anchors=None):
    """Issue an ACR-1 receipt. seed_hex: 64-char hex Ed25519 seed."""
    seed = bytes.fromhex(seed_hex)
    pk = ed25519.publickey(seed).hex()
    body = {
        "schema": SCHEMA,
        "issuer": _signer_block(issuer_name, pk),
        "subject": {"kind": subject_kind, "refs": sorted(refs)},
        "claim": claim,
        "anchors": sorted(anchors or []),
        "issued_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
        "nonce": os.urandom(8).hex(),
    }
    sig = ed25519.sign(canonical(body), seed)
    return dict(body, signature=sig.hex())


def _check_signer(signer, signature, payload_bytes, label):
    if signer.get("key_id") != key_id(signer.get("pubkey", "")):
        return False, f"{label}: key_id does not match pubkey"
    try:
        ok = ed25519.verify(bytes.fromhex(signature), payload_bytes,
                            bytes.fromhex(signer["pubkey"]))
    except Exception:
        ok = False
    if not ok:
        return False, f"{label}: signature invalid"
    return True, "ok"


def verify(receipt):
    """Verify an ACR-1 receipt -> (bool, reason)."""
    if receipt.get("schema") != SCHEMA:
        return False, f"schema must be '{SCHEMA}'"
    body = {k: v for k, v in receipt.items()
            if k not in ("signature", "countersig")}
    if "signature" not in receipt:
        return False, "missing signature"
    return _check_signer(receipt.get("issuer", {}), receipt["signature"],
                         canonical(body), "issuer")


def countersign(receipt, signer_name, seed_hex):
    """Add a counterparty signature over the issuer-signed document
    (receipt minus any existing countersig). Returns a new dict."""
    seed = bytes.fromhex(seed_hex)
    pk = ed25519.publickey(seed).hex()
    payload = {k: v for k, v in receipt.items() if k != "countersig"}
    sig = ed25519.sign(canonical(payload), seed)
    out = dict(receipt)
    out["countersig"] = {"signer": _signer_block(signer_name, pk),
                         "signature": sig.hex()}
    return out


def verify_countersig(receipt):
    cs = receipt.get("countersig")
    if not cs:
        return False, "no countersig"
    payload = {k: v for k, v in receipt.items() if k != "countersig"}
    return _check_signer(cs.get("signer", {}), cs["signature"],
                         canonical(payload), "countersig")


def receipt_hash(receipt):
    """sha256 of the full canonical document (incl. signatures) — the value
    later receipts put in `anchors`."""
    return hashlib.sha256(canonical(receipt)).hexdigest()


def check_anchor(receipt, prior_receipt):
    """True if prior_receipt's hash appears in receipt's anchors."""
    return receipt_hash(prior_receipt) in receipt.get("anchors", [])
