#!/usr/bin/env python3
"""Conformance vector capture for plexus (ARCHITECTURE.md §7.2).

Drives the Python Reticulum reference (RNS) with a deterministic identity to
produce the byte-exact wire vectors the Rust implementation must reproduce. The
vectors printed here are embedded as constants in the Rust conformance tests
(plexus-crypto, plexus-core/codec, plexus-gateway).

Usage:

    python3 -m venv .venv && .venv/bin/pip install rns
    .venv/bin/python crates/plexus-vectors/capture/capture.py

The identity, destination, and signature vectors are fully deterministic. The
announce's `random_hash` is generated by RNS per-announce, so the committed
announce vector is a pinned one-time capture; re-running prints a fresh — and
equally valid — announce that the Rust `AnnouncePayload::verify` also accepts.
"""

import hashlib
import os
import sys
import tempfile

PRV = bytes(range(64))  # x25519_priv(32) || ed25519_seed(32)
APP, ASPECT = "plexus", "test"
APP_DATA = b"hello"
SIGN_MSG = b"plexus conformance vector v0"


def hx(b: bytes) -> str:
    return b.hex()


def main() -> None:
    import RNS

    ident = RNS.Identity(create_keys=False)
    ident.load_private_key(PRV)

    print("# identity (deterministic)")
    print("priv   =", hx(PRV))
    print("pub    =", hx(ident.get_public_key()))
    print("hash   =", hx(ident.hash))
    print("signmsg=", hx(SIGN_MSG))
    print("sig    =", hx(ident.sign(SIGN_MSG)))

    print(f"# destination '{APP}.{ASPECT}' (deterministic)")
    print("namehash=", hx(hashlib.sha256(f"{APP}.{ASPECT}".encode()).digest()[:10]))
    print("address =", hx(RNS.Destination.hash(ident, APP, ASPECT)))

    print("# announce (random_hash varies per run)")
    cfg = tempfile.mkdtemp()
    with open(os.path.join(cfg, "config"), "w") as f:
        f.write(
            "[reticulum]\n  enable_transport = False\n  share_instance = False\n"
            "  panic_on_interface_error = False\n[logging]\n  loglevel = 0\n[interfaces]\n"
        )
    RNS.Reticulum(configdir=cfg)
    dest = RNS.Destination(ident, RNS.Destination.IN, RNS.Destination.SINGLE, APP, ASPECT)

    captured = {}

    def outbound(packet, *args, **kwargs):
        packet.pack()
        captured["raw"] = packet.raw
        captured["data"] = packet.data
        return True

    RNS.Transport.outbound = outbound
    dest.announce(app_data=APP_DATA)

    print("dest     =", hx(dest.hash))
    print("anndata  =", hx(captured["data"]))
    print("packetraw=", hx(captured["raw"]))

    sys.stdout.flush()
    os._exit(0)  # skip RNS background-thread teardown


if __name__ == "__main__":
    main()
