//! Bootnode error types.

/// Errors returned by bootnode operations.
#[derive(Debug, thiserror::Error)]
pub enum BootnodeError {
    /// The latest snapshot is older than the consumer-configured maximum.
    ///
    /// Emitted by consumers (e.g., operator bootstrap per §S.19), never
    /// constructed inside this crate.
    #[error("snapshot too stale: age {age_secs}s exceeds max {max_allowed}s")]
    SnapshotTooStale {
        /// Age of the snapshot in seconds.
        age_secs: u64,
        /// Consumer-configured staleness ceiling.
        max_allowed: u64,
    },

    /// On-disk snapshot has an unrecognised `format_version`.
    #[error("unsupported snapshot format version: {0}")]
    UnsupportedSnapshotFormat(u8),

    /// On-disk delta entry has an unrecognised `format_version`.
    #[error("unsupported delta format version: {0}")]
    UnsupportedDeltaFormat(u8),

    /// Filesystem I/O error (e.g. parent directory creation).
    #[error("io: {0}")]
    Io(#[from] std::io::Error),

    /// redb database error.
    #[error("redb: {0}")]
    Database(#[from] redb::DatabaseError),

    /// redb table error.
    #[error("redb table: {0}")]
    Table(#[from] redb::TableError),

    /// redb transaction error.
    #[error("redb transaction: {0}")]
    Transaction(Box<redb::TransactionError>),

    /// redb storage error.
    #[error("redb storage: {0}")]
    Storage(#[from] redb::StorageError),

    /// redb commit error.
    #[error("redb commit: {0}")]
    Commit(#[from] redb::CommitError),

    /// Failed to encode a record for on-disk storage.
    #[error("serialize: {0}")]
    SerializeFailed(#[source] rmp_serde::encode::Error),

    /// On-disk record could not be decoded (corrupt or incompatible format).
    #[error("deserialize: {0}")]
    DeserializeFailed(#[source] rmp_serde::decode::Error),

    /// Deserialized cert bytes failed validation.
    #[error("invalid DaCert data: {0}")]
    InvalidCertData(String),

    /// Snapshot body exceeds the safety limit.
    #[error("snapshot body too large: {size} bytes exceeds max {max} bytes")]
    SnapshotBodyTooLarge {
        /// Actual body size in bytes.
        size: usize,
        /// Configured maximum.
        max: usize,
    },

    /// `header.body_len` does not match the actual body slice length.
    #[error("snapshot body_len mismatch: header says {header_body_len}, actual {actual}")]
    SnapshotBodyLenMismatch {
        /// Value in the header.
        header_body_len: u64,
        /// Actual body slice length.
        actual: u64,
    },

    /// A delta field exceeds the safety limit.
    #[error("delta field `{field}` too large: {size} bytes exceeds max {max} bytes")]
    DeltaFieldTooLarge {
        /// Which field violated the limit.
        field: &'static str,
        /// Actual size in bytes.
        size: usize,
        /// Configured maximum.
        max: usize,
    },

    /// `fetch_range` was called with a window wider than the configured limit.
    #[error("fetch_range too large: requested {requested} entries exceeds max {max}")]
    FetchRangeTooLarge {
        /// Number of entries the caller asked for (`to_seq - from_seq`).
        requested: u64,
        /// Configured maximum window width.
        max: u64,
    },
}

impl From<redb::TransactionError> for BootnodeError {
    fn from(e: redb::TransactionError) -> Self {
        Self::Transaction(Box::new(e))
    }
}

impl From<rmp_serde::encode::Error> for BootnodeError {
    fn from(e: rmp_serde::encode::Error) -> Self {
        Self::SerializeFailed(e)
    }
}

impl From<rmp_serde::decode::Error> for BootnodeError {
    fn from(e: rmp_serde::decode::Error) -> Self {
        Self::DeserializeFailed(e)
    }
}
