#ifndef RETICULUM_IDENTITY_H
#define RETICULUM_IDENTITY_H

// ============================================================================
// reticulum_identity.{h,cpp} — a real, wire-compatible Reticulum (RNS) node
// identity for T1, used by rnode_bridge.cpp to send genuine self-announces.
//
// Added 2026-07-30. rnode_bridge.h previously documented that TX of real
// RNS announces "requires a full implementation that doesn't fit on
// device" and instead required the OPERATOR to supply an arbitrary
// destination hash to announce -- which was never actually correct RNS
// usage: an announce is a proof that the sender holds the private key for
// the destination it's announcing, so a node can only ever announce ITS
// OWN destination, never an arbitrary hash it doesn't hold the key for.
// This module gives T1 a real, persisted Ed25519+X25519 identity (reusing
// ota_signed.h's primitives) and builds byte-exact RNS announce packets.
//
// Verified against the REAL `RNS` Python reference implementation (pip
// install rns) end-to-end -- not just self-consistency: a full announce
// was generated with a deterministic identity/time/random-hash and every
// field (name_hash, identity_hash, destination_hash, signed_data,
// signature, final packed bytes) matched byte-for-byte. See
// test/test_reticulum_identity/test_reticulum_identity.cpp.
//
// Scope, stated honestly: this implements Identity + self-announce only --
// same as rnode_bridge.h's original scope note, LXMF / Link encryption /
// ratchets are NOT implemented (ratchet=b"", context_flag=FLAG_UNSET,
// matching a real RNS node that simply hasn't enabled ratchets). A real
// peer can discover this sensor's destination via its announce, verify
// the signature, and record a path to it -- establishing an encrypted
// Link to actually talk to it is a separate, larger undertaking this
// does not attempt.
// ============================================================================

#include <cstdint>
#include <cstddef>

#define RNS_HASHLEN            32   // full_hash = SHA-256
#define RNS_TRUNCATED_HASHLEN  16   // identity hash / destination hash
#define RNS_NAME_HASHLEN       10
#define RNS_RANDOM_HASHLEN     10
#define RNS_PUBKEY_LEN         64   // X25519(32) + Ed25519(32)
#define RNS_SIG_LEN            64

// L1 header (see rnode_bridge.h) + ANNOUNCE data. Sized generously for the
// max app_data this firmware would ever attach (sensor ID + status, well
// under 100 bytes) -- RNode's own MTU is 500.
#define RNS_ANNOUNCE_MAX_BYTES  256

// ---------------------------------------------------------------------------
// Hashing (RNS's Identity.full_hash / truncated_hash — plain SHA-256)
// ---------------------------------------------------------------------------
void rns_full_hash(const uint8_t* data, size_t len, uint8_t out[RNS_HASHLEN]);
void rns_truncated_hash(const uint8_t* data, size_t len, uint8_t out[RNS_TRUNCATED_HASHLEN]);

// ---------------------------------------------------------------------------
// Identity
// ---------------------------------------------------------------------------
struct RnsIdentity {
    uint8_t x25519_priv[32];    // = seed (RFC 7748 convention)
    uint8_t x25519_pub[32];
    uint8_t ed25519_sk[64];     // expanded form (seed || pubkey), see ota_signed.h
    uint8_t ed25519_pub[32];
    uint8_t hash[RNS_TRUNCATED_HASHLEN];   // truncated_hash(x25519_pub || ed25519_pub)
};

// Derive an identity from two 32-byte seeds (caller-supplied so this stays
// pure/testable -- on real hardware, seed with esp_fill_random()).
void rns_identity_from_seeds(const uint8_t ed25519_seed[32], const uint8_t x25519_seed[32],
                             RnsIdentity* out);

// Load the persisted identity from NVS, or generate + persist a fresh one
// (via hardware RNG) if none exists yet. Call once at boot. No-op stub
// under UNIT_TEST / native (returns a fixed test identity instead).
void rns_identity_init();
const RnsIdentity* rns_identity_get();

#ifdef UNIT_TEST
void rns_identity_test_set(const RnsIdentity* id);
#endif

// ---------------------------------------------------------------------------
// Destination hashing (RNS's Destination.hash(), no ratchet)
// ---------------------------------------------------------------------------

// name_hash = truncated_hash("app_name.aspect1.aspect2...") -- NO identity
// suffix (RNS computes this specific hash with identity=None).
void rns_name_hash(const char* app_name, const char* const* aspects, int n_aspects,
                   uint8_t out[RNS_NAME_HASHLEN]);

// destination_hash = truncated_hash(name_hash || identity_hash)
void rns_destination_hash(const uint8_t name_hash[RNS_NAME_HASHLEN],
                          const uint8_t identity_hash[RNS_TRUNCATED_HASHLEN],
                          uint8_t out[RNS_TRUNCATED_HASHLEN]);

// ---------------------------------------------------------------------------
// Announce packet (HEADER_1, ANNOUNCE, no transport ID, no ratchet)
// ---------------------------------------------------------------------------

// Builds a complete, real, signed RNS announce packet for `id`'s own
// destination (app_name.aspect1.aspect2...). random_hash10 and unix_time
// are caller-supplied (pure/testable) -- on real hardware, random_hash10
// should be 10 fresh random bytes and unix_time the current wall clock;
// per RNS convention the low 5 bytes of random_hash10 are typically random
// and the high 5 are the timestamp, but this function does not enforce
// that split -- it writes exactly what's passed in the wire's random_hash
// field, byte-compatible with the real protocol either way.
// Returns the packed length, or 0 if out_max is too small.
size_t rns_build_announce(const RnsIdentity* id,
                          const char* app_name, const char* const* aspects, int n_aspects,
                          const uint8_t* app_data, size_t app_data_len,
                          const uint8_t random_hash10[RNS_RANDOM_HASHLEN],
                          uint8_t* out, size_t out_max);

#endif // RETICULUM_IDENTITY_H
