//! Error types for the ECMQV key exchange library.

use core::fmt;

/// Main error type for all ECMQV operations.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum AgreementError {
    /// The provided public key failed validation (infinity, not on curve, or wrong subgroup).
    InvalidPublicKey,
    /// Curve parameters mismatch between parties or missing required static key.
    ParameterMismatch,
    /// The computed shared point is the point at infinity (should never happen with valid keys).
    SharedPointIsIdentity,
    /// Key derivation function failed (e.g., unsupported hash, output length too large).
    KdfError,
    /// Random number generator failed (e.g., insufficient entropy).
    RngError,
    /// The provided hash function is not supported in this build.
    UnsupportedHashFunction,
    /// The output key length requested exceeds the HKDF maximum.
    InvalidKeyLength,
    /// The shared info structure contains invalid data (e.g., lengths mismatch).
    InvalidSharedInfo,
    /// The curve type is not supported by this build.
    UnsupportedCurve,
    /// Internal arithmetic error (e.g., inversion of zero) – should never occur with valid inputs.
    ArithmeticError,
    /// Serialization error (only when `serde` feature is enabled).
    #[cfg(feature = "serde")]
    SerializationError,
}

impl fmt::Display for AgreementError {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::InvalidPublicKey => write!(f, "Invalid public key: point is infinity, off-curve, or not in the correct subgroup"),
            Self::ParameterMismatch => write!(f, "Curve parameter mismatch or missing static key"),
            Self::SharedPointIsIdentity => write!(f, "Shared point is identity – invalid result"),
            Self::KdfError => write!(f, "Key derivation function failed"),
            Self::RngError => write!(f, "Random number generator failure"),
            Self::UnsupportedHashFunction => write!(f, "Hash function not supported"),
            Self::InvalidKeyLength => write!(f, "Requested key length exceeds maximum"),
            Self::InvalidSharedInfo => write!(f, "Invalid shared info structure"),
            Self::UnsupportedCurve => write!(f, "Curve type not supported"),
            Self::ArithmeticError => write!(f, "Internal arithmetic error"),
            #[cfg(feature = "serde")]
            Self::SerializationError => write!(f, "Serialization/deserialization error"),
        }
    }
}

#[cfg(feature = "std")]
impl std::error::Error for AgreementError {
    #[inline]
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

// --- Conversion from lower‑level errors ---

impl From<rand_core::Error> for AgreementError {
    #[inline]
    fn from(_: rand_core::Error) -> Self {
        Self::RngError
    }
}

// curve25519-dalek does not export its own error type; no conversion needed.

// HKDF errors from the `hkdf` crate are mapped to `KdfError` or `InvalidKeyLength`.
#[cfg(feature = "hkdf")]
impl From<hkdf::InvalidLength> for AgreementError {
    #[inline]
    fn from(_: hkdf::InvalidLength) -> Self {
        Self::InvalidKeyLength
    }
}

// Allow conversion from a generic `()` for use in `?` when no other error is available.
impl From<()> for AgreementError {
    #[inline]
    fn from(_: ()) -> Self {
        Self::ArithmeticError
    }
}