//! Fundamental cryptographic types for LatticeArc.
//!
//! Provides core data structures for keys, encrypted data, signed data,
//! and cryptographic context. All types are pure Rust with zero FFI dependencies.

use std::fmt;

use chrono::{DateTime, Utc};
use subtle::{Choice, ConstantTimeEq};
use zeroize::Zeroize;

/// A secure byte container that zeroizes its contents on drop.
///
/// # Security Note
/// Clone is intentionally NOT implemented to prevent creating
/// copies of sensitive data that might not be properly zeroized.
/// If you need to share the data, use `as_slice()` to get a reference.
pub struct ZeroizedBytes {
    data: Vec<u8>,
}

impl ZeroizedBytes {
    /// Creates a new `ZeroizedBytes` from raw byte data.
    #[must_use]
    pub fn new(data: Vec<u8>) -> Self {
        Self { data }
    }

    /// Returns the data as a byte slice.
    #[must_use]
    pub fn as_slice(&self) -> &[u8] {
        &self.data
    }

    /// Returns the length of the data in bytes.
    #[must_use]
    pub fn len(&self) -> usize {
        self.data.len()
    }

    /// Returns `true` if the data is empty.
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.data.is_empty()
    }
}

impl Drop for ZeroizedBytes {
    fn drop(&mut self) {
        self.data.zeroize();
    }
}

impl fmt::Debug for ZeroizedBytes {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ZeroizedBytes").field("data", &"[REDACTED]").finish()
    }
}

impl AsRef<[u8]> for ZeroizedBytes {
    fn as_ref(&self) -> &[u8] {
        &self.data
    }
}

impl ConstantTimeEq for ZeroizedBytes {
    fn ct_eq(&self, other: &Self) -> Choice {
        self.data.ct_eq(&other.data)
    }
}

/// A public key (asymmetric). Distinct newtype for type safety.
///
/// Wrapping the inner `Vec<u8>` prevents accidental confusion with other
/// byte vectors (ciphertexts, signatures, hashes) in function signatures.
/// Construct via [`PublicKey::new`] or `From<Vec<u8>>`; borrow the raw bytes
/// via [`PublicKey::as_slice`] or [`AsRef<[u8]>`].
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PublicKey(Vec<u8>);

impl PublicKey {
    /// Creates a new `PublicKey` from raw byte data.
    #[must_use]
    pub fn new(data: Vec<u8>) -> Self {
        Self(data)
    }

    /// Returns the public key bytes as a slice.
    #[must_use]
    pub fn as_slice(&self) -> &[u8] {
        &self.0
    }

    /// Consumes the `PublicKey` and returns the underlying `Vec<u8>`.
    #[must_use]
    pub fn into_bytes(self) -> Vec<u8> {
        self.0
    }

    /// Returns the length of the public key in bytes.
    #[must_use]
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if the public key is empty.
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
}

impl AsRef<[u8]> for PublicKey {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl From<Vec<u8>> for PublicKey {
    fn from(v: Vec<u8>) -> Self {
        Self(v)
    }
}

/// A private (asymmetric) key with automatic zeroization on drop.
///
/// Newtype wrapper around `ZeroizedBytes` for type safety: this type is
/// distinct from `SymmetricKey`, so they cannot be accidentally confused
/// in function signatures. All behavior (zeroization, constant-time compare,
/// redacted Debug) is inherited from `ZeroizedBytes`.
pub struct PrivateKey(ZeroizedBytes);

impl PrivateKey {
    /// Creates a new `PrivateKey` from raw byte data.
    #[must_use]
    pub fn new(data: Vec<u8>) -> Self {
        Self(ZeroizedBytes::new(data))
    }

    /// Returns the key data as a byte slice.
    #[must_use]
    pub fn as_slice(&self) -> &[u8] {
        self.0.as_slice()
    }

    /// Returns the length of the key in bytes.
    #[must_use]
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if the key is empty.
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
}

impl fmt::Debug for PrivateKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("PrivateKey").field(&"[REDACTED]").finish()
    }
}

impl AsRef<[u8]> for PrivateKey {
    fn as_ref(&self) -> &[u8] {
        self.0.as_slice()
    }
}

impl ConstantTimeEq for PrivateKey {
    fn ct_eq(&self, other: &Self) -> Choice {
        self.0.ct_eq(&other.0)
    }
}

/// A symmetric key with automatic zeroization on drop.
///
/// Newtype wrapper around `ZeroizedBytes` for type safety: this type is
/// distinct from `PrivateKey`, so they cannot be accidentally confused
/// in function signatures.
pub struct SymmetricKey(ZeroizedBytes);

impl SymmetricKey {
    /// Creates a new `SymmetricKey` from raw byte data.
    #[must_use]
    pub fn new(data: Vec<u8>) -> Self {
        Self(ZeroizedBytes::new(data))
    }

    /// Returns the key data as a byte slice.
    #[must_use]
    pub fn as_slice(&self) -> &[u8] {
        self.0.as_slice()
    }

    /// Returns the length of the key in bytes.
    #[must_use]
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if the key is empty.
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
}

impl fmt::Debug for SymmetricKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("SymmetricKey").field(&"[REDACTED]").finish()
    }
}

impl AsRef<[u8]> for SymmetricKey {
    fn as_ref(&self) -> &[u8] {
        self.0.as_slice()
    }
}

impl ConstantTimeEq for SymmetricKey {
    fn ct_eq(&self, other: &Self) -> Choice {
        self.0.ct_eq(&other.0)
    }
}

/// A 256-bit hash output. Newtype for type safety.
///
/// Wrapping the inner `[u8; 32]` prevents accidental confusion with other
/// 32-byte arrays (keys, nonces) in function signatures. Construct via
/// [`HashOutput::new`] or `From<[u8; 32]>`; borrow the raw bytes via
/// [`HashOutput::as_bytes`], [`HashOutput::as_slice`], or [`AsRef<[u8]>`].
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct HashOutput([u8; 32]);

impl HashOutput {
    /// Creates a new `HashOutput` from a 32-byte array.
    #[must_use]
    pub const fn new(data: [u8; 32]) -> Self {
        Self(data)
    }

    /// Returns a reference to the underlying 32-byte array.
    #[must_use]
    pub const fn as_bytes(&self) -> &[u8; 32] {
        &self.0
    }

    /// Returns the hash bytes as a slice.
    #[must_use]
    pub fn as_slice(&self) -> &[u8] {
        &self.0
    }

    /// Consumes the `HashOutput` and returns the underlying 32-byte array.
    #[must_use]
    pub const fn into_bytes(self) -> [u8; 32] {
        self.0
    }
}

impl AsRef<[u8]> for HashOutput {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl From<[u8; 32]> for HashOutput {
    fn from(a: [u8; 32]) -> Self {
        Self(a)
    }
}

/// Metadata associated with encrypted data.
#[derive(Debug, Clone, PartialEq)]
pub struct EncryptedMetadata {
    /// The nonce/IV used for encryption.
    pub nonce: Vec<u8>,
    /// The authentication tag (for AEAD schemes).
    pub tag: Option<Vec<u8>>,
    /// Optional key identifier for key management.
    pub key_id: Option<String>,
}

/// Metadata associated with signed data.
#[derive(Debug, Clone)]
pub struct SignedMetadata {
    /// The signature bytes.
    pub signature: Vec<u8>,
    /// The algorithm used to create the signature.
    pub signature_algorithm: String,
    /// The public key that can verify the signature.
    pub public_key: Vec<u8>,
    /// Optional key identifier for key management.
    pub key_id: Option<String>,
}

/// A generic cryptographic payload with metadata.
#[derive(Debug, Clone, PartialEq)]
pub struct CryptoPayload<T> {
    /// The encrypted or signed data.
    pub data: Vec<u8>,
    /// Scheme-specific metadata.
    pub metadata: T,
    /// The cryptographic scheme used.
    pub scheme: String,
    /// Unix timestamp when the operation was performed.
    pub timestamp: u64,
}

/// Encrypted data with associated metadata.
pub type EncryptedData = CryptoPayload<EncryptedMetadata>;
/// Signed data with associated metadata.
pub type SignedData = CryptoPayload<SignedMetadata>;

/// A cryptographic key pair containing public and private keys.
///
/// # Security Note
/// Clone is intentionally NOT implemented because this struct contains
/// sensitive private key material that should not be copied.
pub struct KeyPair {
    /// The public key component of the key pair.
    public_key: PublicKey,
    /// The private key component of the key pair (sensitive material).
    private_key: PrivateKey,
}

impl Drop for KeyPair {
    fn drop(&mut self) {
        // PrivateKey's inner ZeroizedBytes handles byte zeroization.
        // Explicit drop ensures the struct boundary is covered.
    }
}

impl fmt::Debug for KeyPair {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("KeyPair")
            .field("public_key", &self.public_key)
            .field("private_key", &"[REDACTED]")
            .finish()
    }
}

impl ConstantTimeEq for KeyPair {
    fn ct_eq(&self, other: &Self) -> Choice {
        self.private_key.ct_eq(&other.private_key)
            & self.public_key.as_slice().ct_eq(other.public_key.as_slice())
    }
}

impl KeyPair {
    /// Create a new key pair from public and private key components.
    #[must_use]
    pub fn new(public_key: PublicKey, private_key: PrivateKey) -> Self {
        Self { public_key, private_key }
    }

    /// Get a reference to the public key.
    #[must_use]
    pub fn public_key(&self) -> &PublicKey {
        &self.public_key
    }

    /// Get a reference to the private key.
    #[must_use]
    pub fn private_key(&self) -> &PrivateKey {
        &self.private_key
    }
}

/// Cryptographic mode: hybrid (PQ + classical) or PQ-only.
///
/// This enum is orthogonal to [`SecurityLevel`] (which selects NIST levels 1/3/5).
/// Together they form a 2D configuration: `(SecurityLevel, CryptoMode)`.
///
/// # Examples
///
/// ```rust
/// use latticearc::types::types::{CryptoMode, SecurityLevel};
///
/// // Default: hybrid mode at NIST Level 3
/// let mode = CryptoMode::default();
/// assert_eq!(mode, CryptoMode::Hybrid);
///
/// // PQ-only for CNSA 2.0 compliance
/// let mode = CryptoMode::PqOnly;
/// ```
#[non_exhaustive]
#[derive(
    Debug, Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(kani, derive(kani::Arbitrary))]
pub enum CryptoMode {
    /// Hybrid mode: PQ + classical algorithms for defense-in-depth.
    /// This is the recommended mode during the post-quantum transition.
    #[default]
    Hybrid,
    /// PQ-only mode: pure post-quantum algorithms, no classical component.
    /// Required for CNSA 2.0 compliance. Use with any `SecurityLevel`.
    PqOnly,
}

/// Security level for cryptographic operations.
///
/// Selects the NIST security level (1/3/5) which determines algorithm parameters.
/// The cryptographic mode (hybrid vs PQ-only) is controlled separately via
/// [`CryptoMode`]. All levels default to hybrid mode.
///
/// Higher levels provide stronger protection but may impact performance.
#[non_exhaustive]
#[derive(
    Debug, Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "lowercase")]
#[cfg_attr(kani, derive(kani::Arbitrary))]
pub enum SecurityLevel {
    /// NIST Level 1 (128-bit equivalent).
    /// Uses ML-KEM-512 (+ X25519 in hybrid mode), ML-DSA-44 (+ Ed25519 in hybrid mode).
    /// Suitable for resource-constrained devices and general use.
    Standard,
    /// NIST Level 3 (192-bit equivalent). (default)
    /// Uses ML-KEM-768 (+ X25519 in hybrid mode), ML-DSA-65 (+ Ed25519 in hybrid mode).
    /// Recommended for most applications.
    #[default]
    High,
    /// NIST Level 5 (256-bit equivalent).
    /// Uses ML-KEM-1024 (+ X25519 in hybrid mode), ML-DSA-87 (+ Ed25519 in hybrid mode).
    /// For high-value assets and long-term security.
    Maximum,
}

/// Compliance mode for cryptographic operations.
///
/// Controls which regulatory compliance requirements are enforced at runtime.
/// Some modes require specific compile-time feature flags (e.g., `fips` feature
/// for FIPS 140-3 validated backend).
///
/// # Examples
///
/// ```rust
/// use latticearc::types::types::{ComplianceMode, fips_available};
///
/// // Default mode has no restrictions
/// let mode = ComplianceMode::Default;
/// assert!(!mode.requires_fips());
/// assert!(mode.allows_hybrid());
///
/// // FIPS mode requires the `fips` feature
/// let fips = ComplianceMode::Fips140_3;
/// assert!(fips.requires_fips());
///
/// // Check if FIPS backend is compiled in
/// let _available = fips_available();
/// ```
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(kani, derive(kani::Arbitrary))]
pub enum ComplianceMode {
    /// No compliance restrictions (default).
    /// All algorithms and modes are available.
    #[default]
    Default,
    /// FIPS 140-3 compliance.
    /// Requires the `fips` feature at compile time for a validated backend.
    /// Allows hybrid (PQ + classical) algorithms.
    Fips140_3,
    /// CNSA 2.0 compliance.
    /// Requires the `fips` feature and `CryptoMode::PqOnly`.
    /// Disallows hybrid algorithms — only pure post-quantum schemes are permitted.
    Cnsa2_0,
}

impl ComplianceMode {
    /// Returns `true` if this compliance mode requires the `fips` feature.
    #[must_use]
    pub const fn requires_fips(&self) -> bool {
        matches!(self, Self::Fips140_3 | Self::Cnsa2_0)
    }

    /// Returns `true` if this compliance mode allows hybrid (PQ + classical) algorithms.
    ///
    /// CNSA 2.0 requires PQ-only algorithms; all other modes allow hybrid.
    #[must_use]
    pub const fn allows_hybrid(&self) -> bool {
        !matches!(self, Self::Cnsa2_0)
    }
}

/// Returns `true` if the FIPS 140-3 validated backend is compiled in.
///
/// This checks whether the crate was built with `features = ["fips"]`.
/// When `false`, attempting to use `ComplianceMode::Fips140_3` or `ComplianceMode::Cnsa2_0`
/// will return an error at validation time with a helpful rebuild message.
///
/// # Examples
///
/// ```rust
/// use latticearc::types::types::fips_available;
///
/// if fips_available() {
///     println!("FIPS 140-3 backend is active");
/// } else {
///     println!("Using default (non-FIPS) backend");
/// }
/// ```
#[must_use]
pub const fn fips_available() -> bool {
    cfg!(feature = "fips")
}

/// Performance optimization preference.
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(kani, derive(kani::Arbitrary))]
pub enum PerformancePreference {
    /// Prioritize throughput over memory usage.
    Speed,
    /// Prioritize memory efficiency over speed.
    Memory,
    /// Balance between speed and memory usage.
    #[default]
    Balanced,
}

/// Predefined use cases for automatic scheme selection.
///
/// The library selects optimal algorithms based on the use case requirements:
/// security level, performance characteristics, and compliance needs.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum UseCase {
    // ========================================================================
    // Communication
    // ========================================================================
    /// Real-time messaging with low latency requirements.
    /// Uses ML-KEM-768 for balanced security and performance.
    SecureMessaging,
    /// Email encryption for at-rest and in-transit protection.
    /// Uses ML-KEM-1024 for long-term confidentiality.
    EmailEncryption,
    /// VPN tunnel encryption requiring high throughput.
    /// Uses ML-KEM-768 with performance optimizations.
    VpnTunnel,
    /// API request/response encryption.
    /// Uses ML-KEM-768 for low-latency operations.
    ApiSecurity,

    // ========================================================================
    // Storage
    // ========================================================================
    /// Large file encryption at rest.
    /// Uses ML-KEM-1024 for long-term storage security.
    FileStorage,
    /// Database column or row encryption.
    /// Uses ML-KEM-768 for frequent access patterns.
    DatabaseEncryption,
    /// Cloud storage encryption.
    /// Uses ML-KEM-1024 for data potentially stored for years.
    CloudStorage,
    /// Backup and archive encryption.
    /// Uses ML-KEM-1024 for maximum long-term security.
    BackupArchive,
    /// Configuration and secrets encryption.
    /// Uses ML-KEM-768 for application secrets.
    ConfigSecrets,

    // ========================================================================
    // Authentication & Identity
    // ========================================================================
    /// User or service authentication.
    /// Uses ML-DSA-87 for maximum signature security.
    Authentication,
    /// Session token generation and validation.
    /// Uses ML-KEM-768 with short expiration.
    SessionToken,
    /// Digital certificate signing.
    /// Uses ML-DSA-87 for certificate authorities.
    DigitalCertificate,
    /// Secure key exchange protocols.
    /// Uses ML-KEM-1024 for key agreement.
    KeyExchange,

    // ========================================================================
    // Financial & Legal
    // ========================================================================
    /// High-integrity financial transaction signing.
    /// Uses ML-DSA-87 + Ed25519 hybrid for compliance.
    FinancialTransactions,
    /// Legal document signing with non-repudiation.
    /// Uses ML-DSA-87 for legally binding signatures.
    LegalDocuments,
    /// Blockchain and distributed ledger transactions.
    /// Uses ML-DSA-65 for on-chain efficiency.
    BlockchainTransaction,

    // ========================================================================
    // Regulated Industries
    // ========================================================================
    /// Healthcare records (HIPAA compliance).
    /// Uses ML-KEM-1024 with audit logging.
    HealthcareRecords,
    /// Government classified information.
    /// Uses ML-KEM-1024 + ML-DSA-87 (highest security).
    GovernmentClassified,
    /// Payment card industry (PCI-DSS).
    /// Uses ML-KEM-1024 for cardholder data.
    PaymentCard,

    // ========================================================================
    // IoT & Embedded
    // ========================================================================
    /// IoT device communication (constrained resources).
    /// Uses ML-KEM-512 for resource-limited devices.
    IoTDevice,
    /// Firmware signing and verification.
    /// Uses ML-DSA-65 for update integrity.
    FirmwareSigning,

    // ========================================================================
    // General Purpose
    // ========================================================================
    /// Audit log encryption (append-only).
    /// Uses ML-KEM-768 with integrity verification.
    AuditLog,
}

/// Category of cryptographic scheme.
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(kani, derive(kani::Arbitrary))]
pub enum CryptoScheme {
    /// Hybrid PQC + classical for defense in depth.
    Hybrid,
    /// Symmetric AES-256-GCM encryption (FIPS validated).
    Symmetric,
    /// Symmetric ChaCha20-Poly1305 encryption (non-FIPS, RFC 8439).
    SymmetricChaCha20,
    /// Classical asymmetric (e.g., Ed25519).
    Asymmetric,
    /// Pure post-quantum without classical fallback.
    PostQuantum,
}

/// Strongly-typed signature scheme identifier.
///
/// Provides compile-time safety over the stringly-typed
/// [`SignedMetadata::signature_algorithm`] field. The `as_str`/`FromStr` round-trip
/// preserves the exact wire format used in serialized signatures, so on-disk
/// formats remain backward compatible with prior versions that used raw strings.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SignatureScheme {
    /// Ed25519 (RFC 8032).
    Ed25519,
    /// ML-DSA-44 (FIPS 204, NIST Level 2).
    MlDsa44,
    /// ML-DSA-65 (FIPS 204, NIST Level 3).
    MlDsa65,
    /// ML-DSA-87 (FIPS 204, NIST Level 5).
    MlDsa87,
    /// SLH-DSA-SHAKE-128s (FIPS 205).
    SlhDsaShake128s,
    /// SLH-DSA-SHAKE-192s (FIPS 205).
    SlhDsaShake192s,
    /// SLH-DSA-SHAKE-256s (FIPS 205).
    SlhDsaShake256s,
    /// FN-DSA-512 (draft FIPS 206 / Falcon — NIST Level 1).
    FnDsa512,
    /// FN-DSA-1024 (draft FIPS 206 / Falcon — NIST Level 5).
    FnDsa1024,
    /// Hybrid ML-DSA-44 + Ed25519 signature.
    HybridMlDsa44Ed25519,
    /// Hybrid ML-DSA-65 + Ed25519 signature (default hybrid).
    HybridMlDsa65Ed25519,
    /// Hybrid ML-DSA-87 + Ed25519 signature.
    HybridMlDsa87Ed25519,
}

impl SignatureScheme {
    /// Return the canonical wire-format string for this scheme.
    ///
    /// These values are used in serialized `SignedMetadata::signature_algorithm`
    /// and must remain stable for backward compatibility with stored signatures.
    #[must_use]
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::Ed25519 => "ed25519",
            Self::MlDsa44 => "ml-dsa-44",
            Self::MlDsa65 => "ml-dsa-65",
            Self::MlDsa87 => "ml-dsa-87",
            Self::SlhDsaShake128s => "slh-dsa-shake-128s",
            Self::SlhDsaShake192s => "slh-dsa-shake-192s",
            Self::SlhDsaShake256s => "slh-dsa-shake-256s",
            Self::FnDsa512 => "fn-dsa-512",
            Self::FnDsa1024 => "fn-dsa-1024",
            Self::HybridMlDsa44Ed25519 => "hybrid-ml-dsa-44-ed25519",
            Self::HybridMlDsa65Ed25519 => "hybrid-ml-dsa-65-ed25519",
            Self::HybridMlDsa87Ed25519 => "hybrid-ml-dsa-87-ed25519",
        }
    }
}

impl fmt::Display for SignatureScheme {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}

impl std::str::FromStr for SignatureScheme {
    type Err = crate::types::error::TypeError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ed25519" => Ok(Self::Ed25519),
            "ml-dsa-44" => Ok(Self::MlDsa44),
            "ml-dsa-65" => Ok(Self::MlDsa65),
            "ml-dsa-87" => Ok(Self::MlDsa87),
            "slh-dsa-shake-128s" => Ok(Self::SlhDsaShake128s),
            "slh-dsa-shake-192s" => Ok(Self::SlhDsaShake192s),
            "slh-dsa-shake-256s" => Ok(Self::SlhDsaShake256s),
            // "fn-dsa" without a suffix historically meant Level 512; preserve the
            // legacy wire value by mapping both forms to `FnDsa512`.
            "fn-dsa" | "fn-dsa-512" => Ok(Self::FnDsa512),
            "fn-dsa-1024" => Ok(Self::FnDsa1024),
            "hybrid-ml-dsa-44-ed25519" => Ok(Self::HybridMlDsa44Ed25519),
            "hybrid-ml-dsa-65-ed25519" => Ok(Self::HybridMlDsa65Ed25519),
            "hybrid-ml-dsa-87-ed25519" => Ok(Self::HybridMlDsa87Ed25519),
            other => Err(crate::types::error::TypeError::UnknownScheme(other.to_string())),
        }
    }
}

/// Context for cryptographic operations.
///
/// Carries configuration and metadata that influences scheme selection
/// and operation behavior.
#[derive(Debug, Clone)]
pub struct CryptoContext {
    /// Security level for operations.
    ///
    /// Consumer: `SchemeSelector::select_encryption_scheme()`, `select_signature_scheme()`
    pub security_level: SecurityLevel,
    /// Performance optimization preference.
    ///
    /// Consumer: `SchemeSelector::select_encryption_scheme()`
    pub performance_preference: PerformancePreference,
    /// Optional use case for automatic scheme selection.
    ///
    /// Consumer: `CryptoPolicyEngine::select_encryption_scheme_typed()`
    pub use_case: Option<UseCase>,
    /// Whether hardware acceleration is enabled.
    ///
    /// Consumer: None — reserved for future hardware-aware selection
    pub hardware_acceleration: bool,
    /// Timestamp when the context was created.
    ///
    /// Consumer: `unified_api::audit::AuditEvent` — operation timestamping
    pub timestamp: DateTime<Utc>,
}

impl Default for CryptoContext {
    fn default() -> Self {
        Self {
            security_level: SecurityLevel::default(),
            performance_preference: PerformancePreference::default(),
            use_case: None,
            hardware_acceleration: true,
            timestamp: Utc::now(),
        }
    }
}

/// Selection mode for cryptographic algorithm selection.
///
/// Either a `UseCase` (recommended), a `SecurityLevel` (manual control),
/// or a `ForcedScheme` (explicit override).
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
pub enum AlgorithmSelection {
    /// Select algorithm based on use case (recommended).
    /// The library will choose the optimal algorithm for this use case.
    UseCase(UseCase),
    /// Select algorithm based on security level (manual control).
    /// Use this when your use case doesn't fit predefined options.
    SecurityLevel(SecurityLevel),
    /// Force a specific cryptographic scheme category.
    /// Bypasses automatic selection and uses the specified scheme type directly.
    ForcedScheme(CryptoScheme),
}

impl Default for AlgorithmSelection {
    fn default() -> Self {
        Self::SecurityLevel(SecurityLevel::High)
    }
}

// Formal verification with Kani
#[cfg(kani)]
mod kani_proofs {
    use super::*;

    /// Proves that the default SecurityLevel is High (NIST Level 3).
    /// Security property: if a developer doesn't explicitly choose a level,
    /// they get strong (192-bit) security, not the weakest option.
    #[kani::proof]
    fn security_level_default_is_high() {
        let default = SecurityLevel::default();
        kani::assert(
            default == SecurityLevel::High,
            "Default SecurityLevel must be High (NIST Level 3)",
        );
    }

    /// Proves that the default ComplianceMode has no restrictions.
    #[kani::proof]
    fn compliance_mode_default_is_unrestricted() {
        let default = ComplianceMode::default();
        kani::assert(
            default == ComplianceMode::Default,
            "Default ComplianceMode must be Default (unrestricted)",
        );
        kani::assert(!default.requires_fips(), "Default mode must not require FIPS");
        kani::assert(default.allows_hybrid(), "Default mode must allow hybrid");
    }

    /// Proves that CNSA 2.0 requires FIPS.
    #[kani::proof]
    fn cnsa_requires_fips() {
        let cnsa = ComplianceMode::Cnsa2_0;
        kani::assert(cnsa.requires_fips(), "CNSA 2.0 must require FIPS");
    }

    /// Proves that CNSA 2.0 disallows hybrid algorithms.
    #[kani::proof]
    fn cnsa_disallows_hybrid() {
        let cnsa = ComplianceMode::Cnsa2_0;
        kani::assert(!cnsa.allows_hybrid(), "CNSA 2.0 must disallow hybrid");
    }

    /// Proves requires_fips() is true IFF mode is Fips140_3 or Cnsa2_0.
    /// Exhaustive over all ComplianceMode variants.
    #[kani::proof]
    fn compliance_mode_requires_fips_exhaustive() {
        let mode: ComplianceMode = kani::any();
        let requires = mode.requires_fips();
        let expected = matches!(mode, ComplianceMode::Fips140_3 | ComplianceMode::Cnsa2_0);
        kani::assert(requires == expected, "requires_fips() iff Fips140_3 or Cnsa2_0");
    }

    /// Proves allows_hybrid() is true IFF mode is NOT Cnsa2_0.
    /// Security: CNSA 2.0 mandates PQ-only algorithms.
    #[kani::proof]
    fn compliance_mode_allows_hybrid_exhaustive() {
        let mode: ComplianceMode = kani::any();
        let allows = mode.allows_hybrid();
        let expected = !matches!(mode, ComplianceMode::Cnsa2_0);
        kani::assert(allows == expected, "allows_hybrid() iff not Cnsa2_0");
    }

    /// Proves default PerformancePreference is Balanced (not Speed).
    /// Security: prevents accidental classical-only fallback via Speed default.
    #[kani::proof]
    fn performance_preference_default_is_balanced() {
        let default = PerformancePreference::default();
        kani::assert(
            default == PerformancePreference::Balanced,
            "Default PerformancePreference must be Balanced",
        );
    }
}

#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, deprecated)]
mod tests {
    use super::*;

    // --- ZeroizedBytes tests ---

    #[test]
    fn test_zeroized_bytes_new_stores_data_succeeds() {
        let data = vec![1u8, 2, 3, 4, 5];
        let zb = ZeroizedBytes::new(data.clone());
        assert_eq!(zb.as_slice(), &data);
    }

    #[test]
    fn test_zeroized_bytes_len_returns_correct_length_has_correct_size() {
        let zb = ZeroizedBytes::new(vec![0u8; 32]);
        assert_eq!(zb.len(), 32);
        assert!(!zb.is_empty());
    }

    #[test]
    fn test_zeroized_bytes_empty_has_zero_length_has_correct_size() {
        let zb = ZeroizedBytes::new(vec![]);
        assert_eq!(zb.len(), 0);
        assert!(zb.is_empty());
    }

    #[test]
    fn test_zeroized_bytes_as_ref_returns_slice_succeeds() {
        let data = vec![10u8, 20, 30];
        let zb = ZeroizedBytes::new(data.clone());
        let slice: &[u8] = zb.as_ref();
        assert_eq!(slice, &data);
    }

    #[test]
    fn test_zeroized_bytes_debug_redacts_content_succeeds() {
        let zb = ZeroizedBytes::new(vec![1, 2, 3]);
        let debug = format!("{:?}", zb);
        assert!(debug.contains("ZeroizedBytes"));
    }

    // --- EncryptedMetadata tests ---

    #[test]
    fn test_encrypted_metadata_with_tag_sets_fields_succeeds() {
        let meta = EncryptedMetadata {
            nonce: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
            tag: Some(vec![0xAA; 16]),
            key_id: Some("key-001".to_string()),
        };
        assert_eq!(meta.nonce.len(), 12);
        assert!(meta.tag.is_some());
        assert_eq!(meta.key_id.as_deref(), Some("key-001"));
    }

    #[test]
    fn test_encrypted_metadata_without_tag_sets_none_tag_succeeds() {
        let meta = EncryptedMetadata { nonce: vec![0u8; 12], tag: None, key_id: None };
        assert!(meta.tag.is_none());
        assert!(meta.key_id.is_none());
    }

    #[test]
    fn test_encrypted_metadata_eq_compares_all_fields_succeeds() {
        let meta1 = EncryptedMetadata { nonce: vec![1, 2, 3], tag: None, key_id: None };
        let meta2 = meta1.clone();
        assert_eq!(meta1, meta2);
    }

    // --- SignedMetadata tests ---

    #[test]
    fn test_signed_metadata_clone_debug_work_correctly_succeeds() {
        let meta = SignedMetadata {
            signature: vec![0xBB; 64],
            signature_algorithm: "ML-DSA-65".to_string(),
            public_key: vec![0xCC; 32],
            key_id: Some("sig-key-001".to_string()),
        };
        let cloned = meta.clone();
        assert_eq!(cloned.signature_algorithm, "ML-DSA-65");
        assert_eq!(cloned.public_key.len(), 32);

        let debug = format!("{:?}", meta);
        assert!(debug.contains("SignedMetadata"));
    }

    // --- CryptoPayload tests ---

    #[test]
    fn test_crypto_payload_clone_eq_work_correctly_succeeds() {
        let payload: CryptoPayload<EncryptedMetadata> = CryptoPayload {
            data: vec![1, 2, 3],
            metadata: EncryptedMetadata { nonce: vec![0u8; 12], tag: None, key_id: None },
            scheme: "AES-256-GCM".to_string(),
            timestamp: 1234567890,
        };
        let cloned = payload.clone();
        assert_eq!(payload, cloned);
        assert_eq!(payload.scheme, "AES-256-GCM");
        assert_eq!(payload.timestamp, 1234567890);
    }

    // --- KeyPair tests ---

    #[test]
    fn test_keypair_new_stores_keys_succeeds() {
        let pk = PublicKey::new(vec![1u8; 32]);
        let sk = PrivateKey::new(vec![2u8; 64]);
        let kp = KeyPair::new(pk.clone(), sk);
        assert_eq!(kp.public_key(), &pk);
        assert_eq!(kp.private_key().len(), 64);
    }

    #[test]
    fn test_keypair_debug_redacts_secret_succeeds() {
        let kp = KeyPair::new(PublicKey::new(vec![1u8; 32]), PrivateKey::new(vec![2u8; 32]));
        let debug = format!("{:?}", kp);
        assert!(debug.contains("KeyPair"));
    }

    // --- SecurityLevel tests ---

    #[test]
    fn test_security_level_default_is_standard_succeeds() {
        assert_eq!(SecurityLevel::default(), SecurityLevel::High);
    }

    #[test]
    fn test_security_level_variants_produce_correct_bit_security_succeeds() {
        let variants = vec![SecurityLevel::Standard, SecurityLevel::High, SecurityLevel::Maximum];
        for v in &variants {
            assert_eq!(*v, v.clone());
        }
        assert_ne!(SecurityLevel::Standard, SecurityLevel::Maximum);
    }

    #[test]
    fn test_security_level_debug_produces_nonempty_string_succeeds() {
        let debug = format!("{:?}", SecurityLevel::Maximum);
        assert!(debug.contains("Maximum"));
    }

    // --- CryptoMode tests ---

    #[test]
    fn test_crypto_mode_default_is_hybrid() {
        assert_eq!(CryptoMode::default(), CryptoMode::Hybrid);
    }

    #[test]
    fn test_crypto_mode_serde_roundtrip() {
        let pq = CryptoMode::PqOnly;
        let json = serde_json::to_string(&pq).unwrap();
        assert_eq!(json, "\"pq-only\"");
        let parsed: CryptoMode = serde_json::from_str(&json).unwrap();
        assert_eq!(parsed, CryptoMode::PqOnly);

        let hybrid = CryptoMode::Hybrid;
        let json = serde_json::to_string(&hybrid).unwrap();
        assert_eq!(json, "\"hybrid\"");
        let parsed: CryptoMode = serde_json::from_str(&json).unwrap();
        assert_eq!(parsed, CryptoMode::Hybrid);
    }

    #[test]
    fn test_crypto_mode_serde_rejects_unknown_string() {
        let result = serde_json::from_str::<CryptoMode>("\"classic\"");
        assert!(result.is_err());
    }

    #[test]
    fn test_crypto_mode_variants_are_distinct() {
        assert_ne!(CryptoMode::Hybrid, CryptoMode::PqOnly);
    }

    // --- PerformancePreference tests ---

    #[test]
    fn test_performance_preference_default_is_balanced_succeeds() {
        assert_eq!(PerformancePreference::default(), PerformancePreference::Balanced);
    }

    #[test]
    fn test_performance_preference_variants_are_distinct_are_unique() {
        assert_ne!(PerformancePreference::Speed, PerformancePreference::Memory);
        assert_eq!(PerformancePreference::Speed, PerformancePreference::Speed.clone());
    }

    // --- UseCase tests ---

    #[test]
    fn test_use_case_variants_produce_correct_descriptions_is_documented() {
        let cases = vec![
            UseCase::SecureMessaging,
            UseCase::EmailEncryption,
            UseCase::VpnTunnel,
            UseCase::ApiSecurity,
            UseCase::FileStorage,
            UseCase::DatabaseEncryption,
            UseCase::CloudStorage,
            UseCase::BackupArchive,
            UseCase::ConfigSecrets,
            UseCase::Authentication,
            UseCase::SessionToken,
            UseCase::DigitalCertificate,
            UseCase::KeyExchange,
            UseCase::FinancialTransactions,
            UseCase::LegalDocuments,
            UseCase::BlockchainTransaction,
            UseCase::HealthcareRecords,
            UseCase::GovernmentClassified,
            UseCase::PaymentCard,
            UseCase::IoTDevice,
            UseCase::FirmwareSigning,
            UseCase::AuditLog,
        ];
        for c in &cases {
            assert_eq!(*c, c.clone());
        }
        assert_ne!(UseCase::SecureMessaging, UseCase::FileStorage);
    }

    // --- CryptoScheme tests ---

    #[test]
    fn test_crypto_scheme_variants_are_distinct_are_unique() {
        let schemes = vec![
            CryptoScheme::Hybrid,
            CryptoScheme::Symmetric,
            CryptoScheme::Asymmetric,
            CryptoScheme::PostQuantum,
        ];
        for s in &schemes {
            assert_eq!(*s, s.clone());
        }
        assert_ne!(CryptoScheme::Hybrid, CryptoScheme::Symmetric);
    }

    // --- CryptoContext tests ---

    #[test]
    fn test_crypto_context_default_sets_expected_fields_succeeds() {
        let ctx = CryptoContext::default();
        assert_eq!(ctx.security_level, SecurityLevel::High);
        assert_eq!(ctx.performance_preference, PerformancePreference::Balanced);
        assert!(ctx.use_case.is_none());
        assert!(ctx.hardware_acceleration);
    }

    #[test]
    fn test_crypto_context_clone_debug_work_correctly_succeeds() {
        let ctx = CryptoContext::default();
        let cloned = ctx.clone();
        assert_eq!(cloned.security_level, ctx.security_level);
        let debug = format!("{:?}", ctx);
        assert!(debug.contains("CryptoContext"));
    }

    // --- AlgorithmSelection tests ---

    #[test]
    fn test_algorithm_selection_default_is_automatic_succeeds() {
        let sel = AlgorithmSelection::default();
        assert_eq!(sel, AlgorithmSelection::SecurityLevel(SecurityLevel::High));
    }

    #[test]
    fn test_algorithm_selection_use_case_sets_use_case_field_succeeds() {
        let sel = AlgorithmSelection::UseCase(UseCase::FileStorage);
        assert_eq!(sel, AlgorithmSelection::UseCase(UseCase::FileStorage));
        assert_ne!(sel, AlgorithmSelection::default());
    }

    #[test]
    fn test_algorithm_selection_forced_scheme_sets_forced_field_succeeds() {
        let sel = AlgorithmSelection::ForcedScheme(CryptoScheme::PostQuantum);
        assert_eq!(sel, AlgorithmSelection::ForcedScheme(CryptoScheme::PostQuantum));
        assert_ne!(sel, AlgorithmSelection::default());
        assert_ne!(sel, AlgorithmSelection::UseCase(UseCase::FileStorage));
    }

    // --- ComplianceMode tests ---

    #[test]
    fn test_compliance_mode_default_is_standard_succeeds() {
        let mode = ComplianceMode::default();
        assert_eq!(mode, ComplianceMode::Default);
    }

    #[test]
    fn test_compliance_mode_requires_fips_returns_correct_bool_succeeds() {
        assert!(!ComplianceMode::Default.requires_fips());
        assert!(ComplianceMode::Fips140_3.requires_fips());
        assert!(ComplianceMode::Cnsa2_0.requires_fips());
    }

    #[test]
    fn test_compliance_mode_allows_hybrid_returns_correct_bool_succeeds() {
        assert!(ComplianceMode::Default.allows_hybrid());
        assert!(ComplianceMode::Fips140_3.allows_hybrid());
        assert!(!ComplianceMode::Cnsa2_0.allows_hybrid());
    }

    #[test]
    fn test_compliance_mode_clone_eq_work_correctly_succeeds() {
        let mode = ComplianceMode::Fips140_3;
        assert_eq!(mode, mode.clone());
        assert_ne!(ComplianceMode::Default, ComplianceMode::Fips140_3);
        assert_ne!(ComplianceMode::Fips140_3, ComplianceMode::Cnsa2_0);
    }

    #[test]
    fn test_compliance_mode_debug_produces_nonempty_string_succeeds() {
        let debug = format!("{:?}", ComplianceMode::Fips140_3);
        assert!(debug.contains("Fips140_3"));
    }

    #[test]
    fn test_fips_available_returns_bool_succeeds() {
        let available = fips_available();
        // When built with --all-features or --features fips, this is true.
        // When built without fips feature, this is false.
        #[cfg(feature = "fips")]
        assert!(available);
        #[cfg(not(feature = "fips"))]
        assert!(!available);
    }
}
