"""Build a Visa-TAP-shape interop demo for chain-anchor-v1.

Generates (a) a TAP-style agent-registration record and (b) a real
RFC 9421-signed HTTP request from that agent, hashes both with
manifest-mode JCS canonicalization, and emits the artifacts that
would go into /samples/chain-anchor/. Does NOT broadcast — that's a
separate step gated on the user's go.

Reference: github.com/visa/trusted-agent-protocol (Visa's TAP spec
+ ref impl) and IETF RFC 9421 (HTTP Message Signatures). No code
copied from Visa's repo; only the public RFC 9421 wire format.
"""
import base64
import datetime
import hashlib
import json
import os
import secrets

from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization


# ---- helpers ----

def jcs(o):
    return json.dumps(o, sort_keys=True, separators=(",", ":"),
                      ensure_ascii=False, allow_nan=False).encode("utf-8")


def sha(b):
    return hashlib.sha256(b).digest()


# ---- 1. Agent identity ----

# Deterministic Ed25519 keypair so the sample is reproducible. Seed
# is intentionally a stable string — anyone running this script
# locally gets the same agent identity.
seed = sha(b"satsignal chain-anchor-v1 / visa TAP interop demo / agent#1")
priv = ed25519.Ed25519PrivateKey.from_private_bytes(seed)
pub = priv.public_key()
pub_b = pub.public_bytes(
    encoding=serialization.Encoding.Raw,
    format=serialization.PublicFormat.Raw,
)
pub_b64 = base64.b64encode(pub_b).decode("ascii")


# ---- 2. TAP-style registration record ----

registration = {
    "v": "tap-registration-v1",
    "agent_id": "satsignal-chain-anchor-demo-agent",
    "key_id": "satsignal-tap-demo-ed25519-1",
    "alg": "ed25519",
    "public_key_b64": pub_b64,
    "registry_url": "https://example.invalid/agent-registry/v1/agents",
    "issued_at": "2026-05-12T18:00:00Z",
    "note": ("Synthetic registration record modeled on the Visa TAP "
             "agent-registry shape (github.com/visa/trusted-agent-protocol). "
             "No code copied from Visa's repo; this is a faithful "
             "shape-replica built from the public RFC 9421 standard."),
}
reg_bytes = jcs(registration)
reg_sha = hashlib.sha256(reg_bytes).hexdigest()


# ---- 3. TAP-style RFC 9421 signed request ----

authority = "demo.satsignal.cloud"
path = "/tap-demo/cart/checkout?item=widget&qty=1"
created = 1778611200  # 2026-05-12T18:00:00Z, deterministic
expires = created + 300  # 5-minute validity
nonce = base64.b64encode(b"satsignal-tap-demo-fixed-nonce!").decode("ascii")
key_id = registration["key_id"]
tag = "tap-purchase-intent"

# RFC 9421 signature-input header. Field order matters because it
# becomes part of the signature base. Mirroring tap-agent's shape:
# covered components ("@authority" "@path"), params in fixed order.
sig_params = (
    f'("@authority" "@path");'
    f'created={created};'
    f'expires={expires};'
    f'keyId="{key_id}";'
    f'alg="ed25519";'
    f'nonce="{nonce}";'
    f'tag="{tag}"'
)

# RFC 9421 §2.5 signature base: each covered component on its own line,
# value derived from the request, then the @signature-params line.
signature_base = (
    f'"@authority": {authority}\n'
    f'"@path": {path}\n'
    f'"@signature-params": {sig_params}'
).encode("utf-8")

sig = priv.sign(signature_base)
sig_b64 = base64.b64encode(sig).decode("ascii")

# RFC 9421 wire headers (what a real merchant sees on the request)
signature_input_header = f'sig1={sig_params}'
signature_header = f'sig1=:{sig_b64}:'

signed_request = {
    "v": "tap-signed-request-v1",
    "method": "POST",
    "authority": authority,
    "path": path,
    "signature_input": signature_input_header,
    "signature": signature_header,
    "signature_base_bytes_b64": base64.b64encode(signature_base).decode("ascii"),
    "covered_components": ["@authority", "@path"],
    "alg": "ed25519",
    "key_id": key_id,
    "tag": tag,
    "issued_at": "2026-05-12T18:00:00Z",
    "note": ("Real Ed25519 signature over the RFC 9421 signature "
             "base. Reproducible: anyone re-running this script with "
             "the same seed gets byte-identical signature_base + "
             "signature output."),
}
req_bytes = jcs(signed_request)
req_sha = hashlib.sha256(req_bytes).hexdigest()


# ---- 4. Compute manifest-mode leaves + merkle root ----

items = [
    {"label": "tap-agent-registration",     "sha256_hex": reg_sha},
    {"label": "tap-signed-request-rfc9421", "sha256_hex": req_sha},
]
leaves = [sha(jcs({"label": it["label"], "sha256_hex": it["sha256_hex"]}))
          for it in items]

# Two leaves: pair them directly.
root = sha(leaves[0] + leaves[1]).hex()


# ---- 5. Verify signature locally (sanity check) ----

try:
    pub.verify(sig, signature_base)
    sig_verified = True
except Exception as e:
    sig_verified = False
    raise SystemExit(f"signature did NOT verify: {e}")


# ---- 6. Print dry-run ----

print("== TAP interop demo dry-run ==")
print(f"agent pub_key_b64           : {pub_b64}")
print(f"registration sha256_hex     : {reg_sha}")
print(f"  registration bytes        : {len(reg_bytes)}")
print(f"signed_request sha256_hex   : {req_sha}")
print(f"  request bytes             : {len(req_bytes)}")
print(f"leaf[0] (registration)      : {leaves[0].hex()}")
print(f"leaf[1] (signed_request)    : {leaves[1].hex()}")
print(f"merkle root (locally)       : {root}")
print(f"ed25519 verify              : {sig_verified}")
print()
print("== anchor body to POST /api/v1/anchors ==")
body = {
    "matter_slug": "agent-runs",
    "category": "evidence_bundle",
    "label": "chain-anchor-v1 / Visa TAP interop — registration + RFC 9421 signed request",
    "items": items,
}
print(json.dumps(body, indent=2))

# Stash artifacts for the publish step.
out_dir = "/tmp/tap-demo-out"
os.makedirs(out_dir, exist_ok=True)
open(f"{out_dir}/tap-agent-registration.json", "wb").write(reg_bytes)
open(f"{out_dir}/tap-signed-request.json", "wb").write(req_bytes)
open(f"{out_dir}/anchor-body.json", "w").write(json.dumps(body))
open(f"{out_dir}/expected_root.txt", "w").write(root + "\n")
print(f"\nartifacts staged in {out_dir}/")
