//! Fundamental types, constants, and wire-format primitives used throughout the
//! NWEP library.
//!
//! This module defines the building blocks shared by every other module:
//!
//! - [`Tstamp`] and [`Duration`]: nanosecond-resolution time values.
//! - Time-unit multiplier constants ([`SECONDS`], [`MILLISECONDS`], etc.) for
//!   constructing durations without magic numbers.
//! - Protocol constants: version string, ALPN token, default port, message
//!   size limits, header limits, and stream limits.
//! - Cryptographic size constants for Ed25519 keys/signatures, BLS
//!   keys/signatures, Shamir sharing, and node IDs.
//! - Trust-log and identity constants governing staleness, epoch intervals, and
//!   cache TTLs.
//! - Wire-format message type bytes used in NWEP frame headers.
//! - [`NodeId`]: a 32-byte node identifier derived from an Ed25519 public key.
//! - [`MerkleHash`]: a 32-byte hash used in the distributed trust log's Merkle
//!   tree.
//! - [`Header`]: an HTTP-style name/value pair sent in NWEP request and
//!   response messages.
//! - [`Identity`]: a pair of Ed25519 public key and its derived node ID,
//!   representing a fully identified peer.
//!
//! # Duration arithmetic
//!
//! Durations are plain `u64` nanosecond counts. Use the multiplier constants to
//! avoid magic numbers:
//!
//! ```rust
//! use nwep::types::{Duration, SECONDS, MILLISECONDS};
//!
//! let timeout: Duration = 30 * SECONDS;
//! let retry_delay: Duration = 500 * MILLISECONDS;
//! ```

use crate::ffi;
use std::fmt;

// ---------------------------------------------------------------------------
// Time types
// ---------------------------------------------------------------------------

/// `Tstamp` is a nanosecond-resolution wall-clock timestamp represented as a
/// `u64` count of nanoseconds since the Unix epoch.
///
/// This type is used wherever the C library returns or accepts an absolute time
/// value (e.g., log entry timestamps, checkpoint creation times).
pub type Tstamp = u64;

/// `Duration` is a nanosecond-resolution time interval represented as a `u64`.
///
/// Construct values using the multiplier constants [`NANOSECONDS`],
/// [`MICROSECONDS`], [`MILLISECONDS`], and [`SECONDS`]:
///
/// ```rust
/// use nwep::types::{Duration, SECONDS};
///
/// let five_seconds: Duration = 5 * SECONDS;
/// ```
pub type Duration = u64;

// ---------------------------------------------------------------------------
// Time-unit multipliers
// ---------------------------------------------------------------------------

/// `NANOSECONDS` is the multiplier for nanosecond durations (value: `1`).
///
/// Multiplying by this constant is a no-op but makes intent explicit.
pub const NANOSECONDS: Duration = 1;

/// `MICROSECONDS` is the multiplier for microsecond durations (value: `1_000`
/// nanoseconds).
pub const MICROSECONDS: Duration = 1_000;

/// `MILLISECONDS` is the multiplier for millisecond durations (value:
/// `1_000_000` nanoseconds).
pub const MILLISECONDS: Duration = 1_000_000;

/// `SECONDS` is the multiplier for second durations (value: `1_000_000_000`
/// nanoseconds).
pub const SECONDS: Duration = 1_000_000_000;

// ---------------------------------------------------------------------------
// Protocol identity constants
// ---------------------------------------------------------------------------

/// `PROTO_VER` is the NWEP protocol version string sent in the `:version`
/// pseudo-header of every `connect` request.
pub const PROTO_VER: &str = "WEB/1";

/// `ALPN` is the QUIC Application-Layer Protocol Negotiation token used during
/// the TLS handshake to identify NWEP connections.
pub const ALPN: &str = "WEB/1";

/// `ALPN_LEN` is the byte length of the [`ALPN`] string (5).
pub const ALPN_LEN: usize = 5;

/// `DEFAULT_PORT` is the default UDP port on which NWEP servers listen
/// (6937).
pub const DEFAULT_PORT: u16 = 6937;

// ---------------------------------------------------------------------------
// Message and stream limits
// ---------------------------------------------------------------------------

/// `DEFAULT_MAX_MESSAGE_SIZE` is the default maximum allowed size of a single
/// NWEP message body in bytes (24 MiB). This limit is negotiated during the
/// `connect` handshake via the `max-message-size` header and may be lowered by
/// either peer.
pub const DEFAULT_MAX_MESSAGE_SIZE: usize = 25_165_824;

/// `MAX_HEADERS` is the maximum number of headers permitted in a single NWEP
/// request or response message. Exceeding this limit produces
/// [`crate::error::ERR_PROTO_TOO_MANY_HEADERS`].
pub const MAX_HEADERS: usize = 128;

/// `MAX_HEADER_SIZE` is the maximum allowed byte length of a single header
/// name or value. Exceeding this limit produces
/// [`crate::error::ERR_PROTO_HEADER_TOO_LARGE`].
pub const MAX_HEADER_SIZE: usize = 8192;

/// `DEFAULT_MAX_STREAMS` is the default maximum number of concurrent QUIC
/// streams per connection (100). This is the value advertised in the
/// `max-streams` header during the `connect` handshake.
pub const DEFAULT_MAX_STREAMS: u32 = 100;

/// `DEFAULT_TIMEOUT` is the default request timeout (30 seconds expressed in
/// nanoseconds). Operations that do not complete within this window return
/// [`crate::error::ERR_NETWORK_TIMEOUT`].
pub const DEFAULT_TIMEOUT: Duration = 30 * SECONDS;

// ---------------------------------------------------------------------------
// Cryptographic size constants
// ---------------------------------------------------------------------------

/// `ED25519_PUBKEY_LEN` is the byte length of an Ed25519 public key (32).
pub const ED25519_PUBKEY_LEN: usize = 32;

/// `ED25519_PRIVKEY_LEN` is the byte length of an Ed25519 private key seed
/// (32). The full expanded private key is derived from this seed.
pub const ED25519_PRIVKEY_LEN: usize = 32;

/// `ED25519_SIG_LEN` is the byte length of an Ed25519 signature (64).
pub const ED25519_SIG_LEN: usize = 64;

/// `NODEID_LEN` is the byte length of an NWEP node ID (32). A node ID is
/// derived by hashing an Ed25519 public key.
pub const NODEID_LEN: usize = 32;

/// `CHALLENGE_LEN` is the byte length of a random challenge nonce used in
/// mutual authentication (32 bytes).
pub const CHALLENGE_LEN: usize = 32;

/// `REQUEST_ID_LEN` is the byte length of a request ID (16 bytes). Request IDs
/// are cryptographically random and appear in the `request-id` header.
pub const REQUEST_ID_LEN: usize = 16;

/// `TRACE_ID_LEN` is the byte length of a distributed trace ID (16 bytes).
/// Trace IDs propagate across hops in the `trace-id` header.
pub const TRACE_ID_LEN: usize = 16;

/// `NOTIFY_ID_LEN` is the byte length of a notification ID (16 bytes). Notify
/// IDs uniquely identify server-push notification messages.
pub const NOTIFY_ID_LEN: usize = 16;

// ---------------------------------------------------------------------------
// Address and URL limits
// ---------------------------------------------------------------------------

/// `BASE58_ADDR_LEN` is the maximum byte length of a Base58-encoded NWEP node
/// address string (66 bytes including the null terminator).
pub const BASE58_ADDR_LEN: usize = 66;

/// `URL_MAX_LEN` is the maximum allowed byte length of a URL or `:path` value
/// in an NWEP request (512 bytes).
pub const URL_MAX_LEN: usize = 512;

// ---------------------------------------------------------------------------
// BLS (threshold signature) constants
// ---------------------------------------------------------------------------

/// `BLS_PUBKEY_LEN` is the byte length of a BLS12-381 public key in compressed
/// G2 form (48 bytes).
pub const BLS_PUBKEY_LEN: usize = 48;

/// `BLS_PRIVKEY_LEN` is the byte length of a BLS12-381 private key scalar (32
/// bytes).
pub const BLS_PRIVKEY_LEN: usize = 32;

/// `BLS_SIG_LEN` is the byte length of a BLS12-381 signature in compressed G1
/// form (96 bytes).
pub const BLS_SIG_LEN: usize = 96;

// ---------------------------------------------------------------------------
// Shamir secret-sharing constants
// ---------------------------------------------------------------------------

/// `SHAMIR_MAX_SHARES` is the maximum number of Shamir secret-sharing shares
/// that can be generated for a single secret (255).
pub const SHAMIR_MAX_SHARES: usize = 255;

/// `SHAMIR_MIN_THRESHOLD` is the minimum number of shares required to
/// reconstruct a secret (2). A threshold of 1 would be equivalent to no
/// sharing at all.
pub const SHAMIR_MIN_THRESHOLD: usize = 2;

// ---------------------------------------------------------------------------
// Key lifecycle constants
// ---------------------------------------------------------------------------

/// `KEY_OVERLAP_SECONDS` is the number of seconds during which both the old and
/// new keypair remain valid following a key rotation (300 seconds / 5 minutes).
/// This overlap window allows in-flight connections using the old key to
/// complete gracefully.
pub const KEY_OVERLAP_SECONDS: u64 = 300;

/// `MAX_ACTIVE_KEYS` is the maximum number of simultaneously active keypairs
/// for a single identity during a rotation overlap period (2).
pub const MAX_ACTIVE_KEYS: usize = 2;

// ---------------------------------------------------------------------------
// Trust log / anchor constants
// ---------------------------------------------------------------------------

/// `MAX_ANCHORS` is the maximum number of BLS anchor nodes that can be
/// registered in the trust system (32).
pub const MAX_ANCHORS: usize = 32;

/// `DEFAULT_ANCHOR_THRESHOLD` is the default BLS quorum threshold: at least
/// this many anchor signatures are required to ratify a checkpoint (5).
pub const DEFAULT_ANCHOR_THRESHOLD: usize = 5;

/// `MAX_CHECKPOINTS` is the maximum number of historical checkpoints retained
/// in memory or on disk (168, i.e., one week of hourly checkpoints).
pub const MAX_CHECKPOINTS: usize = 168;

// ---------------------------------------------------------------------------
// Log-entry and Merkle constants
// ---------------------------------------------------------------------------

/// `LOG_ENTRY_MAX_SIZE` is the maximum serialized byte size of a single trust
/// log entry (256 bytes).
pub const LOG_ENTRY_MAX_SIZE: usize = 256;

/// `MERKLE_PROOF_MAX_DEPTH` is the maximum depth of a Merkle inclusion proof
/// path (64 levels), sufficient for a tree with up to 2^64 leaves.
pub const MERKLE_PROOF_MAX_DEPTH: usize = 64;

// ---------------------------------------------------------------------------
// Pool and cache constants
// ---------------------------------------------------------------------------

/// `CACHE_DEFAULT_CAPACITY` is the default maximum number of entries in an
/// in-memory identity or result cache (10,000 entries).
pub const CACHE_DEFAULT_CAPACITY: usize = 10_000;

/// `POOL_MAX_SERVERS` is the maximum number of server endpoints that can be
/// held in a single client connection pool (32).
pub const POOL_MAX_SERVERS: usize = 32;

/// `POOL_HEALTH_CHECK_FAILURES` is the number of consecutive health-check
/// failures before a server is removed from the active pool (3).
pub const POOL_HEALTH_CHECK_FAILURES: usize = 3;

// ---------------------------------------------------------------------------
// Frame / message-type constants
// ---------------------------------------------------------------------------

/// `FRAME_HEADER_SIZE` is the byte size of the fixed NWEP frame header that
/// precedes every message on the wire (4 bytes: a big-endian `u32` length
/// prefix).
pub const FRAME_HEADER_SIZE: usize = 4;

/// `MSG_TYPE_SIZE` is the byte size of the message-type discriminant within an
/// NWEP frame (1 byte).
pub const MSG_TYPE_SIZE: usize = 1;

// ---------------------------------------------------------------------------
// Staleness and epoch constants
// ---------------------------------------------------------------------------

/// `STALENESS_WARNING_NS` is the age beyond which an identity record triggers a
/// staleness warning (1 hour expressed in nanoseconds).
pub const STALENESS_WARNING_NS: Duration = 3600 * SECONDS;

/// `STALENESS_REJECT_NS` is the age beyond which an identity record is rejected
/// outright as too stale to trust (24 hours expressed in nanoseconds).
pub const STALENESS_REJECT_NS: Duration = 86400 * SECONDS;

/// `IDENTITY_CACHE_TTL` is the time-to-live for cached identity records (1 hour
/// in nanoseconds). Entries older than this are re-fetched from the log server.
pub const IDENTITY_CACHE_TTL: Duration = 3600 * SECONDS;

/// `DEFAULT_EPOCH_INTERVAL` is the default interval between trust-log epochs
/// (1 hour in nanoseconds). At each epoch boundary the anchor nodes produce a
/// new BLS checkpoint.
pub const DEFAULT_EPOCH_INTERVAL: Duration = 3600 * SECONDS;

/// `CHECKPOINT_DST` is the domain-separation tag prepended to data before it is
/// signed into a BLS checkpoint, preventing cross-protocol signature reuse.
pub const CHECKPOINT_DST: &str = "WEB/1-CHECKPOINT";

// ---------------------------------------------------------------------------
// Message type discriminants
// ---------------------------------------------------------------------------

/// `MSG_REQUEST` is the wire-format type byte for a client request message
/// (0x00).
pub const MSG_REQUEST: u8 = 0;

/// `MSG_RESPONSE` is the wire-format type byte for a server response message
/// (0x01).
pub const MSG_RESPONSE: u8 = 1;

/// `MSG_STREAM` is the wire-format type byte for a bidirectional streaming data
/// chunk (0x02).
pub const MSG_STREAM: u8 = 2;

/// `MSG_NOTIFY` is the wire-format type byte for a server-initiated push
/// notification (0x03).
pub const MSG_NOTIFY: u8 = 3;

// ---------------------------------------------------------------------------
// NodeId
// ---------------------------------------------------------------------------

/// `NodeId` is the 32-byte identifier that uniquely names a node in the NWEP
/// network.
///
/// A node ID is derived by applying a cryptographic hash to the node's Ed25519
/// public key, so it is bound to the key material and cannot be forged without
/// possession of the private key.
///
/// Node IDs are displayed as lowercase hex strings (64 characters). The
/// all-zero value is reserved and considered invalid; use [`NodeId::is_zero`]
/// to test for it.
///
/// # Example
///
/// ```rust
/// use nwep::types::NodeId;
///
/// let id = NodeId([0u8; 32]);
/// assert!(id.is_zero());
/// println!("{id}"); // 0000...0000
/// ```
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct NodeId(pub [u8; NODEID_LEN]);

impl NodeId {
    /// `is_zero` returns `true` if all bytes of this node ID are zero.
    ///
    /// An all-zero node ID is the uninitialized/invalid sentinel value. Use
    /// this check to guard against unset node IDs before sending them over the
    /// wire.
    pub fn is_zero(&self) -> bool {
        self.0.iter().all(|&b| b == 0)
    }

    /// `as_bytes` returns a reference to the raw 32-byte node ID array.
    ///
    /// Useful when the bytes must be passed to a C API or hashed directly
    /// without copying.
    pub fn as_bytes(&self) -> &[u8; NODEID_LEN] {
        &self.0
    }
}

impl fmt::Display for NodeId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for b in &self.0 {
            write!(f, "{:02x}", b)?;
        }
        Ok(())
    }
}

impl fmt::Debug for NodeId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "NodeId({})", self)
    }
}

impl From<ffi::nwep_nodeid> for NodeId {
    fn from(n: ffi::nwep_nodeid) -> Self {
        NodeId(n.data)
    }
}

impl From<NodeId> for ffi::nwep_nodeid {
    fn from(n: NodeId) -> Self {
        ffi::nwep_nodeid { data: n.0 }
    }
}

// ---------------------------------------------------------------------------
// MerkleHash
// ---------------------------------------------------------------------------

/// `MerkleHash` is a 32-byte SHA-256 digest used as a node value in the
/// distributed trust log's Merkle tree.
///
/// Interior nodes are computed by hashing the concatenation of their left and
/// right children. Leaf nodes are computed by hashing a serialized log entry.
/// The root hash of the Merkle tree is included in BLS checkpoints so that any
/// entry can be proven to belong to the log.
///
/// Like [`NodeId`], `MerkleHash` displays as a lowercase 64-character hex
/// string.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct MerkleHash(pub [u8; 32]);

impl fmt::Display for MerkleHash {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for b in &self.0 {
            write!(f, "{:02x}", b)?;
        }
        Ok(())
    }
}

impl fmt::Debug for MerkleHash {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "MerkleHash({})", self)
    }
}

impl From<ffi::nwep_merkle_hash> for MerkleHash {
    fn from(h: ffi::nwep_merkle_hash) -> Self {
        MerkleHash(h.data)
    }
}

impl From<MerkleHash> for ffi::nwep_merkle_hash {
    fn from(h: MerkleHash) -> Self {
        ffi::nwep_merkle_hash { data: h.0 }
    }
}

// ---------------------------------------------------------------------------
// Header
// ---------------------------------------------------------------------------

/// `Header` is a single HTTP-style name/value metadata pair attached to an
/// NWEP request or response.
///
/// Header names follow the same conventions as HTTP/2 pseudo-headers and
/// lowercase field names. Pseudo-headers (`:method`, `:path`, `:status`,
/// `:version`) must appear before regular headers. See the constants in
/// [`crate::protocol`] for the well-known header name strings.
///
/// Both `name` and `value` are arbitrary UTF-8 strings. The C library enforces
/// a maximum byte length of [`MAX_HEADER_SIZE`] for each field and a maximum
/// count of [`MAX_HEADERS`] per message.
///
/// # Example
///
/// ```rust
/// use nwep::types::Header;
/// use nwep::protocol::HDR_TRACE_ID;
///
/// let h = Header::new(HDR_TRACE_ID, "deadbeef...");
/// assert_eq!(h.name, "trace-id");
/// ```
#[derive(Clone, Debug)]
pub struct Header {
    /// `name` is the header field name (e.g., `"content-type"`, `":method"`).
    pub name: String,
    /// `value` is the header field value (e.g., `"application/octet-stream"`,
    /// `"read"`).
    pub value: String,
}

impl Header {
    /// `new` constructs a [`Header`] from any types that convert into
    /// `String`.
    ///
    /// # Example
    ///
    /// ```rust
    /// use nwep::types::Header;
    ///
    /// let h = Header::new("content-type", "application/json");
    /// ```
    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
        Header { name: name.into(), value: value.into() }
    }
}

// ---------------------------------------------------------------------------
// Identity
// ---------------------------------------------------------------------------

/// `Identity` pairs an Ed25519 public key with its derived [`NodeId`],
/// representing a fully authenticated peer identity.
///
/// The `pubkey` field holds the raw 32-byte Ed25519 public key bytes. The
/// `node_id` field holds the node ID derived from that key. Together they allow
/// the caller to verify both the cryptographic material and the stable
/// network-layer identifier of a remote peer.
///
/// `Identity` values are returned by the server's `on_connect` callback and by
/// client-side peer inspection APIs.
///
/// # Example
///
/// ```rust
/// use nwep::types::Identity;
///
/// let id = Identity::default();
/// assert!(id.node_id.is_zero());
/// ```
#[derive(Clone, Debug, Default)]
pub struct Identity {
    /// `pubkey` is the raw 32-byte Ed25519 public key of the peer.
    pub pubkey: [u8; ED25519_PUBKEY_LEN],
    /// `node_id` is the 32-byte node ID derived from [`Identity::pubkey`].
    pub node_id: NodeId,
}

impl From<ffi::nwep_identity> for Identity {
    fn from(i: ffi::nwep_identity) -> Self {
        Identity { pubkey: i.pubkey, node_id: NodeId(i.nodeid.data) }
    }
}
