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

/// `ERR_CONFIG_FILE_NOT_FOUND` indicates the specified configuration file does
/// not exist on disk.
pub const ERR_CONFIG_FILE_NOT_FOUND: i32 = ffi::NWEP_ERR_CONFIG_FILE_NOT_FOUND;

/// `ERR_CONFIG_PARSE_ERROR` indicates the configuration file exists but could
/// not be parsed (e.g., malformed syntax).
pub const ERR_CONFIG_PARSE_ERROR: i32 = ffi::NWEP_ERR_CONFIG_PARSE_ERROR;

/// `ERR_CONFIG_INVALID_VALUE` indicates a configuration key was present but its
/// value failed validation (e.g., out-of-range integer, invalid address).
pub const ERR_CONFIG_INVALID_VALUE: i32 = ffi::NWEP_ERR_CONFIG_INVALID_VALUE;

/// `ERR_CONFIG_MISSING_REQUIRED` indicates a required configuration key was
/// absent from the configuration file or builder.
pub const ERR_CONFIG_MISSING_REQUIRED: i32 = ffi::NWEP_ERR_CONFIG_MISSING_REQUIRED;

/// `ERR_CONFIG_VALIDATION_FAILED` indicates the configuration passed individual
/// field checks but failed cross-field or semantic validation.
pub const ERR_CONFIG_VALIDATION_FAILED: i32 = ffi::NWEP_ERR_CONFIG_VALIDATION_FAILED;

/// `ERR_NETWORK_CONN_FAILED` indicates that a QUIC connection attempt to the
/// remote peer was rejected or could not be established.
pub const ERR_NETWORK_CONN_FAILED: i32 = ffi::NWEP_ERR_NETWORK_CONN_FAILED;

/// `ERR_NETWORK_CONN_CLOSED` indicates the remote peer closed the connection
/// unexpectedly.
pub const ERR_NETWORK_CONN_CLOSED: i32 = ffi::NWEP_ERR_NETWORK_CONN_CLOSED;

/// `ERR_NETWORK_TIMEOUT` indicates an operation did not complete within the
/// configured deadline.
pub const ERR_NETWORK_TIMEOUT: i32 = ffi::NWEP_ERR_NETWORK_TIMEOUT;

/// `ERR_NETWORK_ADDR_IN_USE` indicates the requested local address/port was
/// already bound by another socket.
pub const ERR_NETWORK_ADDR_IN_USE: i32 = ffi::NWEP_ERR_NETWORK_ADDR_IN_USE;

/// `ERR_NETWORK_ADDR_INVALID` indicates the supplied address string or
/// `sockaddr` could not be parsed or is structurally invalid.
pub const ERR_NETWORK_ADDR_INVALID: i32 = ffi::NWEP_ERR_NETWORK_ADDR_INVALID;

/// `ERR_NETWORK_SOCKET` indicates a low-level socket operation (bind, listen,
/// send, recv) returned an OS error.
pub const ERR_NETWORK_SOCKET: i32 = ffi::NWEP_ERR_NETWORK_SOCKET;

/// `ERR_NETWORK_TLS` indicates a TLS-level error occurred during or after the
/// QUIC handshake (certificate validation, alert, etc.).
pub const ERR_NETWORK_TLS: i32 = ffi::NWEP_ERR_NETWORK_TLS;

/// `ERR_NETWORK_QUIC` indicates a QUIC transport error that is not otherwise
/// classified (stream reset, connection error frame, etc.).
pub const ERR_NETWORK_QUIC: i32 = ffi::NWEP_ERR_NETWORK_QUIC;

/// `ERR_NETWORK_NO_SERVERS` indicates a client pool operation found no healthy
/// server endpoints available to connect to.
pub const ERR_NETWORK_NO_SERVERS: i32 = ffi::NWEP_ERR_NETWORK_NO_SERVERS;

/// `ERR_CRYPTO_KEY_GEN_FAILED` indicates Ed25519 or BLS key generation failed,
/// typically due to an internal OpenSSL error.
pub const ERR_CRYPTO_KEY_GEN_FAILED: i32 = ffi::NWEP_ERR_CRYPTO_KEY_GEN_FAILED;

/// `ERR_CRYPTO_SIGN_FAILED` indicates a signing operation over a message or
/// transcript failed.
pub const ERR_CRYPTO_SIGN_FAILED: i32 = ffi::NWEP_ERR_CRYPTO_SIGN_FAILED;

/// `ERR_CRYPTO_VERIFY_FAILED` indicates a signature verification failed because
/// the signature did not match the message or public key.
pub const ERR_CRYPTO_VERIFY_FAILED: i32 = ffi::NWEP_ERR_CRYPTO_VERIFY_FAILED;

/// `ERR_CRYPTO_HASH_FAILED` indicates a cryptographic hash operation (SHA-256,
/// etc.) returned an internal error.
pub const ERR_CRYPTO_HASH_FAILED: i32 = ffi::NWEP_ERR_CRYPTO_HASH_FAILED;

/// `ERR_CRYPTO_INVALID_KEY` indicates a key structure is malformed, zero, or
/// otherwise unusable.
pub const ERR_CRYPTO_INVALID_KEY: i32 = ffi::NWEP_ERR_CRYPTO_INVALID_KEY;

/// `ERR_CRYPTO_INVALID_SIG` indicates a signature byte sequence is structurally
/// invalid (wrong length, bad encoding) before verification is even attempted.
pub const ERR_CRYPTO_INVALID_SIG: i32 = ffi::NWEP_ERR_CRYPTO_INVALID_SIG;

/// `ERR_CRYPTO_CERT_ERROR` indicates a TLS certificate operation failed (e.g.,
/// self-signed certificate generation or DER encoding error).
pub const ERR_CRYPTO_CERT_ERROR: i32 = ffi::NWEP_ERR_CRYPTO_CERT_ERROR;

/// `ERR_CRYPTO_PUBKEY_MISMATCH` indicates the public key presented by a peer
/// does not match the expected or stored public key.
pub const ERR_CRYPTO_PUBKEY_MISMATCH: i32 = ffi::NWEP_ERR_CRYPTO_PUBKEY_MISMATCH;

/// `ERR_CRYPTO_NODEID_MISMATCH` indicates the node ID derived from a presented
/// public key does not match the expected node ID.
pub const ERR_CRYPTO_NODEID_MISMATCH: i32 = ffi::NWEP_ERR_CRYPTO_NODEID_MISMATCH;

/// `ERR_CRYPTO_CHALLENGE_FAILED` indicates a challenge-response authentication
/// exchange did not produce a valid response.
pub const ERR_CRYPTO_CHALLENGE_FAILED: i32 = ffi::NWEP_ERR_CRYPTO_CHALLENGE_FAILED;

/// `ERR_CRYPTO_SERVER_SIG_INVALID` indicates the handshake transcript signature
/// provided by the server failed verification on the client side.
pub const ERR_CRYPTO_SERVER_SIG_INVALID: i32 = ffi::NWEP_ERR_CRYPTO_SERVER_SIG_INVALID;

/// `ERR_CRYPTO_CLIENT_SIG_INVALID` indicates the handshake transcript signature
/// provided by the client failed verification on the server side.
pub const ERR_CRYPTO_CLIENT_SIG_INVALID: i32 = ffi::NWEP_ERR_CRYPTO_CLIENT_SIG_INVALID;

/// `ERR_CRYPTO_AUTH_TIMEOUT` indicates the mutual authentication handshake did
/// not complete within the required time window.
pub const ERR_CRYPTO_AUTH_TIMEOUT: i32 = ffi::NWEP_ERR_CRYPTO_AUTH_TIMEOUT;

/// `ERR_PROTO_INVALID_MESSAGE` indicates a received frame could not be decoded
/// as a valid NWEP message.
pub const ERR_PROTO_INVALID_MESSAGE: i32 = ffi::NWEP_ERR_PROTO_INVALID_MESSAGE;

/// `ERR_PROTO_INVALID_METHOD` indicates the `:method` header contains a string
/// that is not a recognized NWEP method name.
pub const ERR_PROTO_INVALID_METHOD: i32 = ffi::NWEP_ERR_PROTO_INVALID_METHOD;

/// `ERR_PROTO_INVALID_HEADER` indicates a header name or value fails structural
/// validation (e.g., contains a NUL byte, exceeds size limits).
pub const ERR_PROTO_INVALID_HEADER: i32 = ffi::NWEP_ERR_PROTO_INVALID_HEADER;

/// `ERR_PROTO_MSG_TOO_LARGE` indicates a message body exceeds the negotiated
/// `max-message-size` for the connection.
pub const ERR_PROTO_MSG_TOO_LARGE: i32 = ffi::NWEP_ERR_PROTO_MSG_TOO_LARGE;

/// `ERR_PROTO_STREAM_ERROR` indicates a QUIC stream was reset or encountered an
/// error during a protocol-level read or write.
pub const ERR_PROTO_STREAM_ERROR: i32 = ffi::NWEP_ERR_PROTO_STREAM_ERROR;

/// `ERR_PROTO_INVALID_STATUS` indicates the `:status` header contains a string
/// that is not a recognized NWEP status code.
pub const ERR_PROTO_INVALID_STATUS: i32 = ffi::NWEP_ERR_PROTO_INVALID_STATUS;

/// `ERR_PROTO_CONNECT_REQUIRED` indicates an operation was attempted before the
/// `connect` handshake completed on the connection.
pub const ERR_PROTO_CONNECT_REQUIRED: i32 = ffi::NWEP_ERR_PROTO_CONNECT_REQUIRED;

/// `ERR_PROTO_TOO_MANY_HEADERS` indicates a message contains more headers than
/// the [`crate::types::MAX_HEADERS`] limit allows.
pub const ERR_PROTO_TOO_MANY_HEADERS: i32 = ffi::NWEP_ERR_PROTO_TOO_MANY_HEADERS;

/// `ERR_PROTO_HEADER_TOO_LARGE` indicates a single header name or value exceeds
/// the [`crate::types::MAX_HEADER_SIZE`] byte limit.
pub const ERR_PROTO_HEADER_TOO_LARGE: i32 = ffi::NWEP_ERR_PROTO_HEADER_TOO_LARGE;

/// `ERR_PROTO_0RTT_REJECTED` indicates the server rejected the client's attempt
/// to send data in QUIC 0-RTT (early data), requiring a retry on a fresh
/// connection.
pub const ERR_PROTO_0RTT_REJECTED: i32 = ffi::NWEP_ERR_PROTO_0RTT_REJECTED;

/// `ERR_PROTO_MISSING_HEADER` indicates a required pseudo-header (`:method`,
/// `:path`, `:status`, etc.) was absent from the message.
pub const ERR_PROTO_MISSING_HEADER: i32 = ffi::NWEP_ERR_PROTO_MISSING_HEADER;

/// `ERR_PROTO_ROLE_MISMATCH` indicates an operation was routed to a server
/// whose role does not handle the requested path or method.
pub const ERR_PROTO_ROLE_MISMATCH: i32 = ffi::NWEP_ERR_PROTO_ROLE_MISMATCH;

/// `ERR_PROTO_UNAUTHORIZED` indicates the authenticated client identity is not
/// permitted to perform the requested operation.
pub const ERR_PROTO_UNAUTHORIZED: i32 = ffi::NWEP_ERR_PROTO_UNAUTHORIZED;

/// `ERR_PROTO_PATH_NOT_FOUND` indicates no handler was registered for the
/// requested `:path`.
pub const ERR_PROTO_PATH_NOT_FOUND: i32 = ffi::NWEP_ERR_PROTO_PATH_NOT_FOUND;

/// `ERR_PROTO_VERSION_MISMATCH` indicates the `:version` presented by the
/// remote peer is incompatible with this implementation.
pub const ERR_PROTO_VERSION_MISMATCH: i32 = ffi::NWEP_ERR_PROTO_VERSION_MISMATCH;

/// `ERR_IDENTITY_INVALID_NODEID` indicates a node ID byte sequence is
/// structurally invalid (wrong length, all-zero, etc.).
pub const ERR_IDENTITY_INVALID_NODEID: i32 = ffi::NWEP_ERR_IDENTITY_INVALID_NODEID;

/// `ERR_IDENTITY_INVALID_ADDR` indicates a network address associated with an
/// identity record could not be parsed or is invalid.
pub const ERR_IDENTITY_INVALID_ADDR: i32 = ffi::NWEP_ERR_IDENTITY_INVALID_ADDR;

/// `ERR_IDENTITY_AUTH_FAILED` indicates the identity authentication exchange
/// (challenge/response) did not succeed.
pub const ERR_IDENTITY_AUTH_FAILED: i32 = ffi::NWEP_ERR_IDENTITY_AUTH_FAILED;

/// `ERR_IDENTITY_CHALLENGE_EXPIRED` indicates the challenge nonce is no longer
/// valid because its time window has passed.
pub const ERR_IDENTITY_CHALLENGE_EXPIRED: i32 = ffi::NWEP_ERR_IDENTITY_CHALLENGE_EXPIRED;

/// `ERR_IDENTITY_NO_RECOVERY` indicates no recovery shares are stored for this
/// identity, making key recovery impossible.
pub const ERR_IDENTITY_NO_RECOVERY: i32 = ffi::NWEP_ERR_IDENTITY_NO_RECOVERY;

/// `ERR_IDENTITY_RECOVERY_MISMATCH` indicates the shares provided for recovery
/// reconstruct a key that does not match the stored public key.
pub const ERR_IDENTITY_RECOVERY_MISMATCH: i32 = ffi::NWEP_ERR_IDENTITY_RECOVERY_MISMATCH;

/// `ERR_IDENTITY_INVALID_SHARE` indicates a Shamir secret-sharing share is
/// malformed or has an invalid index.
pub const ERR_IDENTITY_INVALID_SHARE: i32 = ffi::NWEP_ERR_IDENTITY_INVALID_SHARE;

/// `ERR_IDENTITY_SHARE_COMBINE` indicates the Shamir share combination step
/// failed, likely due to insufficient or inconsistent shares.
pub const ERR_IDENTITY_SHARE_COMBINE: i32 = ffi::NWEP_ERR_IDENTITY_SHARE_COMBINE;

/// `ERR_IDENTITY_INVALID_THRESHOLD` indicates a Shamir threshold value is
/// outside the allowed range (must be between 2 and the share count).
pub const ERR_IDENTITY_INVALID_THRESHOLD: i32 = ffi::NWEP_ERR_IDENTITY_INVALID_THRESHOLD;

/// `ERR_IDENTITY_ROTATION_IN_PROGRESS` indicates a key rotation is already
/// underway for this identity; concurrent rotations are not permitted.
pub const ERR_IDENTITY_ROTATION_IN_PROGRESS: i32 = ffi::NWEP_ERR_IDENTITY_ROTATION_IN_PROGRESS;

/// `ERR_IDENTITY_KEY_MISMATCH` indicates the presented signing key does not
/// match the key recorded in the identity log for this node.
pub const ERR_IDENTITY_KEY_MISMATCH: i32 = ffi::NWEP_ERR_IDENTITY_KEY_MISMATCH;

/// `ERR_IDENTITY_REVOKED` indicates the identity has been explicitly revoked
/// and may no longer authenticate.
pub const ERR_IDENTITY_REVOKED: i32 = ffi::NWEP_ERR_IDENTITY_REVOKED;

/// `ERR_STORAGE_FILE_NOT_FOUND` indicates a required data file or directory
/// does not exist at the expected path.
pub const ERR_STORAGE_FILE_NOT_FOUND: i32 = ffi::NWEP_ERR_STORAGE_FILE_NOT_FOUND;

/// `ERR_STORAGE_READ_ERROR` indicates an I/O error occurred while reading from
/// storage.
pub const ERR_STORAGE_READ_ERROR: i32 = ffi::NWEP_ERR_STORAGE_READ_ERROR;

/// `ERR_STORAGE_WRITE_ERROR` indicates an I/O error occurred while writing to
/// storage.
pub const ERR_STORAGE_WRITE_ERROR: i32 = ffi::NWEP_ERR_STORAGE_WRITE_ERROR;

/// `ERR_STORAGE_PERMISSION` indicates a file system permission error prevented
/// reading or writing the target path.
pub const ERR_STORAGE_PERMISSION: i32 = ffi::NWEP_ERR_STORAGE_PERMISSION;

/// `ERR_STORAGE_DISK_FULL` indicates there is insufficient disk space to
/// complete a write operation.
pub const ERR_STORAGE_DISK_FULL: i32 = ffi::NWEP_ERR_STORAGE_DISK_FULL;

/// `ERR_STORAGE_KEY_NOT_FOUND` indicates a lookup by key returned no matching
/// record in the storage backend.
pub const ERR_STORAGE_KEY_NOT_FOUND: i32 = ffi::NWEP_ERR_STORAGE_KEY_NOT_FOUND;

/// `ERR_STORAGE_INDEX_OUT_OF_RANGE` indicates a numeric index (e.g., log entry
/// sequence number) is beyond the bounds of the stored data.
pub const ERR_STORAGE_INDEX_OUT_OF_RANGE: i32 = ffi::NWEP_ERR_STORAGE_INDEX_OUT_OF_RANGE;

/// `ERR_STORAGE_CORRUPTED` indicates stored data failed integrity verification
/// (bad checksum, truncated file, etc.).
pub const ERR_STORAGE_CORRUPTED: i32 = ffi::NWEP_ERR_STORAGE_CORRUPTED;

/// `ERR_TRUST_PARSE_ERROR` indicates a trust log entry or checkpoint document
/// could not be decoded from its serialized form.
pub const ERR_TRUST_PARSE_ERROR: i32 = ffi::NWEP_ERR_TRUST_PARSE_ERROR;

/// `ERR_TRUST_INVALID_ENTRY` indicates a trust log entry is structurally valid
/// but semantically incorrect (e.g., invalid field combination).
pub const ERR_TRUST_INVALID_ENTRY: i32 = ffi::NWEP_ERR_TRUST_INVALID_ENTRY;

/// `ERR_TRUST_INVALID_SIG` indicates a trust log entry or checkpoint carries a
/// signature that fails cryptographic verification.
pub const ERR_TRUST_INVALID_SIG: i32 = ffi::NWEP_ERR_TRUST_INVALID_SIG;

/// `ERR_TRUST_QUORUM_NOT_REACHED` indicates that an operation requiring a BLS
/// threshold quorum did not collect enough valid signatures.
pub const ERR_TRUST_QUORUM_NOT_REACHED: i32 = ffi::NWEP_ERR_TRUST_QUORUM_NOT_REACHED;

/// `ERR_TRUST_INVALID_PROOF` indicates a Merkle inclusion proof presented for a
/// log entry is structurally invalid or does not verify against the root.
pub const ERR_TRUST_INVALID_PROOF: i32 = ffi::NWEP_ERR_TRUST_INVALID_PROOF;

/// `ERR_TRUST_ENTRY_NOT_FOUND` indicates the requested log entry does not exist
/// in the local or remote trust log.
pub const ERR_TRUST_ENTRY_NOT_FOUND: i32 = ffi::NWEP_ERR_TRUST_ENTRY_NOT_FOUND;

/// `ERR_TRUST_CHECKPOINT_STALE` indicates a checkpoint's sequence number is
/// older than the current local state, making it unsafe to apply.
pub const ERR_TRUST_CHECKPOINT_STALE: i32 = ffi::NWEP_ERR_TRUST_CHECKPOINT_STALE;

/// `ERR_TRUST_ANCHOR_UNKNOWN` indicates a checkpoint or proof references an
/// anchor node ID that is not recognized in the local anchor set.
pub const ERR_TRUST_ANCHOR_UNKNOWN: i32 = ffi::NWEP_ERR_TRUST_ANCHOR_UNKNOWN;

/// `ERR_TRUST_DUPLICATE_BINDING` indicates an attempt to register a public-key
/// binding that already exists in the trust log.
pub const ERR_TRUST_DUPLICATE_BINDING: i32 = ffi::NWEP_ERR_TRUST_DUPLICATE_BINDING;

/// `ERR_TRUST_NODE_NOT_FOUND` indicates a node ID lookup in the trust log
/// returned no matching entry.
pub const ERR_TRUST_NODE_NOT_FOUND: i32 = ffi::NWEP_ERR_TRUST_NODE_NOT_FOUND;

/// `ERR_TRUST_ALREADY_REVOKED` indicates an attempt to revoke a node that has
/// already been revoked.
pub const ERR_TRUST_ALREADY_REVOKED: i32 = ffi::NWEP_ERR_TRUST_ALREADY_REVOKED;

/// `ERR_TRUST_INVALID_AUTH` indicates the authorization proof accompanying a
/// trust operation (e.g., key rotation) failed verification.
pub const ERR_TRUST_INVALID_AUTH: i32 = ffi::NWEP_ERR_TRUST_INVALID_AUTH;

/// `ERR_TRUST_UNAUTHORIZED` indicates the requesting node is not authorized to
/// perform the trust operation (e.g., insufficient role).
pub const ERR_TRUST_UNAUTHORIZED: i32 = ffi::NWEP_ERR_TRUST_UNAUTHORIZED;

/// `ERR_TRUST_TYPE_NOT_ALLOWED` indicates the requested trust entry type is not
/// permitted in the current context (e.g., anchor-only operations on a regular
/// server).
pub const ERR_TRUST_TYPE_NOT_ALLOWED: i32 = ffi::NWEP_ERR_TRUST_TYPE_NOT_ALLOWED;

/// `ERR_TRUST_KEY_MISMATCH` indicates the signing key in a trust log entry does
/// not match the key previously recorded for that node.
pub const ERR_TRUST_KEY_MISMATCH: i32 = ffi::NWEP_ERR_TRUST_KEY_MISMATCH;

/// `ERR_TRUST_STORAGE` indicates a storage-layer error occurred while reading
/// or writing the trust log, wrapping a lower-level `ERR_STORAGE_*` condition.
pub const ERR_TRUST_STORAGE: i32 = ffi::NWEP_ERR_TRUST_STORAGE;

/// `ERR_TRUST_LOG_CORRUPTED` indicates the trust log's internal consistency
/// check failed (e.g., broken hash chain or Merkle tree mismatch).
pub const ERR_TRUST_LOG_CORRUPTED: i32 = ffi::NWEP_ERR_TRUST_LOG_CORRUPTED;

/// `ERR_TRUST_EQUIVOCATION` indicates two conflicting entries were detected for
/// the same log position, which is evidence of a double-signing attack.
pub const ERR_TRUST_EQUIVOCATION: i32 = ffi::NWEP_ERR_TRUST_EQUIVOCATION;

/// `ERR_INTERNAL_UNKNOWN` indicates an unexpected internal error with no more
/// specific classification.
pub const ERR_INTERNAL_UNKNOWN: i32 = ffi::NWEP_ERR_INTERNAL_UNKNOWN;

/// `ERR_INTERNAL_NOT_IMPLEMENTED` indicates an operation or code path is not
/// yet implemented in the current build of the library.
pub const ERR_INTERNAL_NOT_IMPLEMENTED: i32 = ffi::NWEP_ERR_INTERNAL_NOT_IMPLEMENTED;

/// `ERR_INTERNAL_INVALID_STATE` indicates the library's internal state machine
/// reached an unexpected or inconsistent state.
pub const ERR_INTERNAL_INVALID_STATE: i32 = ffi::NWEP_ERR_INTERNAL_INVALID_STATE;

/// `ERR_INTERNAL_NULL_PTR` indicates the library received or produced a null
/// pointer where a valid pointer was required.
pub const ERR_INTERNAL_NULL_PTR: i32 = ffi::NWEP_ERR_INTERNAL_NULL_PTR;

/// `ERR_INTERNAL_NOMEM` indicates a dynamic memory allocation failed inside the
/// C library.
pub const ERR_INTERNAL_NOMEM: i32 = ffi::NWEP_ERR_INTERNAL_NOMEM;

/// `ERR_INTERNAL_INVALID_ARG` indicates a function was called with an argument
/// value that the C library considers invalid.
pub const ERR_INTERNAL_INVALID_ARG: i32 = ffi::NWEP_ERR_INTERNAL_INVALID_ARG;

/// `ERR_INTERNAL_CALLBACK_FAILURE` indicates a user-supplied callback returned
/// an error or panicked in a context where the library cannot propagate it.
pub const ERR_INTERNAL_CALLBACK_FAILURE: i32 = ffi::NWEP_ERR_INTERNAL_CALLBACK_FAILURE;

/// `ERR_INTERNAL_NOBUF` indicates an internal buffer is too small to hold the
/// produced output (the operation should be retried with a larger buffer).
pub const ERR_INTERNAL_NOBUF: i32 = ffi::NWEP_ERR_INTERNAL_NOBUF;

/// `ErrorCategory` classifies an [`Error`] into the NWEP subsystem that
/// produced it.
///
/// Each variant corresponds to a group of error codes sharing the same
/// `NWEP_ERR_CAT_*` C constant. The category is determined automatically when
/// constructing an [`Error`] via [`Error::from_code`].
///
/// The `Display` implementation produces the lowercase subsystem name (e.g.,
/// `"network"`, `"crypto"`) which is suitable for logging and structured
/// output.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErrorCategory {
    /// `None` indicates no error (code zero) or an unrecognized category.
    None,
    /// `Config` groups errors related to configuration file parsing and
    /// validation.
    Config,
    /// `Network` groups errors related to QUIC/UDP transport and socket
    /// operations.
    Network,
    /// `Crypto` groups errors related to key generation, signing, verification,
    /// and TLS certificate handling.
    Crypto,
    /// `Protocol` groups errors related to the NWEP application-layer protocol
    /// framing, headers, and method/status validation.
    Protocol,
    /// `Identity` groups errors related to node identity records, challenge
    /// authentication, and Shamir secret sharing.
    Identity,
    /// `Storage` groups errors related to file I/O, key-value lookups, and
    /// data integrity.
    Storage,
    /// `Trust` groups errors related to the distributed trust log, Merkle
    /// proofs, BLS checkpoints, and equivocation detection.
    Trust,
    /// `Internal` groups errors that indicate bugs or unimplemented code paths
    /// inside the C library itself.
    Internal,
}

impl fmt::Display for ErrorCategory {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match self {
            ErrorCategory::None => "none",
            ErrorCategory::Config => "config",
            ErrorCategory::Network => "network",
            ErrorCategory::Crypto => "crypto",
            ErrorCategory::Protocol => "protocol",
            ErrorCategory::Identity => "identity",
            ErrorCategory::Storage => "storage",
            ErrorCategory::Trust => "trust",
            ErrorCategory::Internal => "internal",
        };
        f.write_str(s)
    }
}

/// Converts a raw FFI `nwep_error_category` discriminant into the Rust
/// [`ErrorCategory`] enum. Unknown discriminants map to [`ErrorCategory::None`].
fn category_from_ffi(cat: ffi::nwep_error_category) -> ErrorCategory {
    match cat {
        ffi::nwep_error_category_NWEP_ERR_CAT_CONFIG => ErrorCategory::Config,
        ffi::nwep_error_category_NWEP_ERR_CAT_NETWORK => ErrorCategory::Network,
        ffi::nwep_error_category_NWEP_ERR_CAT_CRYPTO => ErrorCategory::Crypto,
        ffi::nwep_error_category_NWEP_ERR_CAT_PROTOCOL => ErrorCategory::Protocol,
        ffi::nwep_error_category_NWEP_ERR_CAT_IDENTITY => ErrorCategory::Identity,
        ffi::nwep_error_category_NWEP_ERR_CAT_STORAGE => ErrorCategory::Storage,
        ffi::nwep_error_category_NWEP_ERR_CAT_TRUST => ErrorCategory::Trust,
        ffi::nwep_error_category_NWEP_ERR_CAT_INTERNAL => ErrorCategory::Internal,
        _ => ErrorCategory::None,
    }
}

/// `Error` is the main error type returned by fallible NWEP operations.
///
/// An `Error` is constructed from a non-zero integer error code returned by the
/// C library. The constructor calls back into the C library to resolve the
/// human-readable message, category, and fatality flag, so no further lookups
/// are needed by callers.
///
/// # Fatal errors
///
/// When [`Error::fatal`] is `true` the connection or server context that
/// produced the error is no longer usable and must be dropped. Non-fatal errors
/// (e.g., [`ERR_NETWORK_TIMEOUT`] on a single request) may be retried without
/// tearing down the connection.
///
/// # Display format
///
/// ```text
/// [network:1003] connection timed out
/// ```
///
/// The format is `[<category>:<code>] <message>`, which makes it easy to grep
/// logs by subsystem or numeric code.
///
/// # Example
///
/// ```rust
/// use nwep::error::{Error, ERR_CRYPTO_VERIFY_FAILED};
///
/// let err = Error::from_code(ERR_CRYPTO_VERIFY_FAILED);
/// assert_eq!(err.category, nwep::error::ErrorCategory::Crypto);
/// eprintln!("{err}");
/// ```
#[derive(Debug, Clone)]
pub struct Error {
    /// `code` is the raw integer error code as returned by the C library.
    /// Zero is never a valid error code; `Error` values are only constructed
    /// for non-zero codes.
    pub code: i32,

    /// `category` identifies the NWEP subsystem that produced this error.
    /// Use it for coarse-grained error handling (e.g., distinguishing network
    /// transients from crypto failures).
    pub category: ErrorCategory,

    /// `fatal` is `true` when the C library considers the connection or context
    /// unrecoverable. Callers must drop the associated [`crate::Client`] or
    /// [`crate::Server`] when this flag is set.
    pub fatal: bool,

    /// `message` is the English-language description of the error retrieved
    /// from the C library via `nwep_strerror`. Falls back to `"unknown error"`
    /// if the C library returns a null pointer.
    pub message: String,
}

impl Error {
    /// `from_code` constructs an [`Error`] by querying the C library for the
    /// human-readable message, fatality status, and category associated with
    /// `code`.
    ///
    /// This is the primary constructor and is called automatically by the
    /// internal [`check`] and [`check_ssize`] helpers whenever a C function
    /// returns a non-zero error code.
    ///
    /// # Example
    ///
    /// ```rust
    /// use nwep::error::{Error, ERR_NETWORK_TIMEOUT};
    ///
    /// let err = Error::from_code(ERR_NETWORK_TIMEOUT);
    /// println!("{}", err.message);
    /// ```
    pub fn from_code(code: i32) -> Self {
        let message = unsafe {
            let ptr = ffi::nwep_strerror(code);
            if ptr.is_null() {
                String::from("unknown error")
            } else {
                CStr::from_ptr(ptr).to_string_lossy().into_owned()
            }
        };
        let fatal = unsafe { ffi::nwep_err_is_fatal(code) != 0 };
        let cat_ffi = unsafe { ffi::nwep_err_category(code) };
        let category = category_from_ffi(cat_ffi);
        Error {
            code,
            category,
            fatal,
            message,
        }
    }

    /// `to_status` maps this error's code to the corresponding NWEP protocol
    /// status string (e.g., `"unauthorized"`, `"not_found"`).
    ///
    /// The returned string is a `'static` reference into the C library's string
    /// table, so it is valid for the lifetime of the process. If the C library
    /// returns a null pointer the fallback value `"internal_error"` is
    /// returned.
    ///
    /// This is useful when an error needs to be communicated back to a remote
    /// client as a protocol-level status code in a response header.
    ///
    /// # Example
    ///
    /// ```rust
    /// use nwep::error::{Error, ERR_NETWORK_CONN_FAILED};
    ///
    /// let err = Error::from_code(ERR_NETWORK_CONN_FAILED);
    /// assert_eq!(err.to_status(), "internal_error");
    /// ```
    pub fn to_status(&self) -> &'static str {
        unsafe {
            let ptr = ffi::nwep_err_to_status(self.code);
            if ptr.is_null() {
                "internal_error"
            } else {
                CStr::from_ptr(ptr).to_str().unwrap_or("internal_error")
            }
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[{}:{}] {}", self.category, self.code, self.message)
    }
}

impl std::error::Error for Error {}

/// `check` converts a C library return code into a Rust `Result`.
///
/// Returns `Ok(())` when `code` is zero and `Err(Error::from_code(code))`
/// otherwise. This is the standard adapter used throughout the crate whenever a
/// C function signals success/failure via its return value.
pub(crate) fn check(code: i32) -> Result<(), Error> {
    if code == 0 {
        Ok(())
    } else {
        Err(Error::from_code(code))
    }
}

/// `check_ssize` converts a C library signed-size return value into a Rust
/// `Result<usize, Error>`.
///
/// Non-negative values indicate the number of bytes transferred and are
/// returned as `Ok(n as usize)`. Negative values are error codes and are
/// converted via [`Error::from_code`]. This mirrors the POSIX convention where
/// `ssize_t` functions return `-errno` on failure.
pub(crate) fn check_ssize(n: ffi::nwep_ssize) -> Result<usize, Error> {
    if n >= 0 {
        Ok(n as usize)
    } else {
        Err(Error::from_code(n as i32))
    }
}
