//! SPHINCS+ (SLH-DSA) hash-based digital signatures.
//!
//! SPHINCS+ is a stateless hash-based signature scheme selected by NIST as
//! FIPS 205 (SLH-DSA). Unlike lattice-based schemes like Dilithium, SPHINCS+
//! relies only on the security of hash functions, making it a conservative
//! backup option.
//!
//! # Security Properties
//!
//! - **Hash-Based**: Security relies only on SHA3/SHAKE, no lattice assumptions
//! - **Stateless**: No state management required (unlike XMSS/LMS)
//! - **Conservative**: Backup algorithm if lattice schemes are compromised
//!
//! # Trade-offs
//!
//! | Variant | Security | Signature Size | Public Key | Speed |
//! |---------|----------|----------------|------------|-------|
//! | SPHINCS+-128s | 128-bit | 7,856 bytes | 32 bytes | Slow |
//! | SPHINCS+-128f | 128-bit | 17,088 bytes | 32 bytes | Fast |
//! | SPHINCS+-192s | 192-bit | 16,224 bytes | 48 bytes | Slow |
//! | SPHINCS+-192f | 192-bit | 35,664 bytes | 48 bytes | Fast |
//! | SPHINCS+-256s | 256-bit | 29,792 bytes | 64 bytes | Slow |
//! | SPHINCS+-256f | 256-bit | 49,856 bytes | 64 bytes | Fast |
//!
//! # Usage
//!
//! ```rust
//! use synor_crypto::sphincs::{SphincsKeypair, SphincsVariant};
//!
//! // Generate keypair (128-bit security, small signatures)
//! let keypair = SphincsKeypair::generate(SphincsVariant::Shake128s);
//!
//! // Sign a message
//! let message = b"Hello, post-quantum world!";
//! let signature = keypair.sign(message);
//!
//! // Verify signature
//! assert!(keypair.public_key().verify(message, &signature).is_ok());
//!
//! // Signature is ~7.8KB for 128s variant
//! println!("Signature size: {} bytes", signature.len());
//! ```

use pqcrypto_sphincsplus::sphincsshake128ssimple as sphincs128s;
use pqcrypto_sphincsplus::sphincsshake192ssimple as sphincs192s;
use pqcrypto_sphincsplus::sphincsshake256ssimple as sphincs256s;
use pqcrypto_traits::sign::{
    DetachedSignature, PublicKey as PqPublicKey, SecretKey as PqSecretKey,
};
use thiserror::Error;
use zeroize::{Zeroize, ZeroizeOnDrop};

/// SPHINCS+ variant selection.
///
/// All variants use SHAKE (SHA3-based) for hashing.
/// 's' variants have smaller signatures but are slower.
/// 'f' variants are faster but have larger signatures.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SphincsVariant {
    /// 128-bit security, small signatures (~7.8KB)
    Shake128s,
    /// 192-bit security, small signatures (~16KB)
    Shake192s,
    /// 256-bit security, small signatures (~30KB)
    Shake256s,
}

impl SphincsVariant {
    /// Returns the expected signature size in bytes.
    pub const fn signature_size(&self) -> usize {
        match self {
            SphincsVariant::Shake128s => 7856,
            SphincsVariant::Shake192s => 16224,
            SphincsVariant::Shake256s => 29792,
        }
    }

    /// Returns the expected public key size in bytes.
    pub const fn public_key_size(&self) -> usize {
        match self {
            SphincsVariant::Shake128s => 32,
            SphincsVariant::Shake192s => 48,
            SphincsVariant::Shake256s => 64,
        }
    }

    /// Returns the expected secret key size in bytes.
    pub const fn secret_key_size(&self) -> usize {
        match self {
            SphincsVariant::Shake128s => 64,
            SphincsVariant::Shake192s => 96,
            SphincsVariant::Shake256s => 128,
        }
    }

    /// Returns the security level in bits.
    pub const fn security_level(&self) -> u16 {
        match self {
            SphincsVariant::Shake128s => 128,
            SphincsVariant::Shake192s => 192,
            SphincsVariant::Shake256s => 256,
        }
    }

    /// Returns the NIST security category.
    pub const fn nist_category(&self) -> u8 {
        match self {
            SphincsVariant::Shake128s => 1,
            SphincsVariant::Shake192s => 3,
            SphincsVariant::Shake256s => 5,
        }
    }
}

impl Default for SphincsVariant {
    fn default() -> Self {
        SphincsVariant::Shake128s
    }
}

/// SPHINCS+ public key.
#[derive(Clone)]
pub struct SphincsPublicKey {
    variant: SphincsVariant,
    bytes: Vec<u8>,
}

impl SphincsPublicKey {
    /// Creates a public key from bytes.
    pub fn from_bytes(variant: SphincsVariant, bytes: &[u8]) -> Result<Self, SphincsError> {
        if bytes.len() != variant.public_key_size() {
            return Err(SphincsError::InvalidKeySize {
                expected: variant.public_key_size(),
                got: bytes.len(),
            });
        }
        Ok(Self {
            variant,
            bytes: bytes.to_vec(),
        })
    }

    /// Returns the public key bytes.
    pub fn as_bytes(&self) -> &[u8] {
        &self.bytes
    }

    /// Returns the variant used.
    pub fn variant(&self) -> SphincsVariant {
        self.variant
    }

    /// Verifies a signature against this public key.
    pub fn verify(&self, message: &[u8], signature: &SphincsSignature) -> Result<(), SphincsError> {
        if signature.variant != self.variant {
            return Err(SphincsError::VariantMismatch);
        }

        match self.variant {
            SphincsVariant::Shake128s => {
                let pk = sphincs128s::PublicKey::from_bytes(&self.bytes)
                    .map_err(|_| SphincsError::InvalidPublicKey)?;
                let sig = sphincs128s::DetachedSignature::from_bytes(&signature.bytes)
                    .map_err(|_| SphincsError::InvalidSignature)?;
                sphincs128s::verify_detached_signature(&sig, message, &pk)
                    .map_err(|_| SphincsError::VerificationFailed)
            }
            SphincsVariant::Shake192s => {
                let pk = sphincs192s::PublicKey::from_bytes(&self.bytes)
                    .map_err(|_| SphincsError::InvalidPublicKey)?;
                let sig = sphincs192s::DetachedSignature::from_bytes(&signature.bytes)
                    .map_err(|_| SphincsError::InvalidSignature)?;
                sphincs192s::verify_detached_signature(&sig, message, &pk)
                    .map_err(|_| SphincsError::VerificationFailed)
            }
            SphincsVariant::Shake256s => {
                let pk = sphincs256s::PublicKey::from_bytes(&self.bytes)
                    .map_err(|_| SphincsError::InvalidPublicKey)?;
                let sig = sphincs256s::DetachedSignature::from_bytes(&signature.bytes)
                    .map_err(|_| SphincsError::InvalidSignature)?;
                sphincs256s::verify_detached_signature(&sig, message, &pk)
                    .map_err(|_| SphincsError::VerificationFailed)
            }
        }
    }
}

impl std::fmt::Debug for SphincsPublicKey {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("SphincsPublicKey")
            .field("variant", &self.variant)
            .field("bytes", &hex::encode(&self.bytes[..8.min(self.bytes.len())]))
            .finish()
    }
}

/// SPHINCS+ secret key (zeroized on drop).
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct SphincsSecretKey {
    #[zeroize(skip)]
    variant: SphincsVariant,
    bytes: Vec<u8>,
}

impl SphincsSecretKey {
    /// Creates a secret key from bytes.
    pub fn from_bytes(variant: SphincsVariant, bytes: &[u8]) -> Result<Self, SphincsError> {
        if bytes.len() != variant.secret_key_size() {
            return Err(SphincsError::InvalidKeySize {
                expected: variant.secret_key_size(),
                got: bytes.len(),
            });
        }
        Ok(Self {
            variant,
            bytes: bytes.to_vec(),
        })
    }

    /// Returns the secret key bytes.
    pub fn as_bytes(&self) -> &[u8] {
        &self.bytes
    }

    /// Returns the variant used.
    pub fn variant(&self) -> SphincsVariant {
        self.variant
    }
}

/// SPHINCS+ signature.
#[derive(Clone)]
pub struct SphincsSignature {
    variant: SphincsVariant,
    bytes: Vec<u8>,
}

impl SphincsSignature {
    /// Creates a signature from bytes.
    pub fn from_bytes(variant: SphincsVariant, bytes: &[u8]) -> Result<Self, SphincsError> {
        if bytes.len() != variant.signature_size() {
            return Err(SphincsError::InvalidSignatureSize {
                expected: variant.signature_size(),
                got: bytes.len(),
            });
        }
        Ok(Self {
            variant,
            bytes: bytes.to_vec(),
        })
    }

    /// Returns the signature bytes.
    pub fn as_bytes(&self) -> &[u8] {
        &self.bytes
    }

    /// Returns the signature length.
    pub fn len(&self) -> usize {
        self.bytes.len()
    }

    /// Returns true if the signature is empty.
    pub fn is_empty(&self) -> bool {
        self.bytes.is_empty()
    }

    /// Returns the variant used.
    pub fn variant(&self) -> SphincsVariant {
        self.variant
    }
}

impl std::fmt::Debug for SphincsSignature {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("SphincsSignature")
            .field("variant", &self.variant)
            .field("size", &self.bytes.len())
            .finish()
    }
}

/// SPHINCS+ keypair.
pub struct SphincsKeypair {
    variant: SphincsVariant,
    public_key: SphincsPublicKey,
    secret_key: SphincsSecretKey,
}

impl SphincsKeypair {
    /// Generates a new random keypair.
    pub fn generate(variant: SphincsVariant) -> Self {
        match variant {
            SphincsVariant::Shake128s => {
                let (pk, sk) = sphincs128s::keypair();
                Self {
                    variant,
                    public_key: SphincsPublicKey {
                        variant,
                        bytes: pk.as_bytes().to_vec(),
                    },
                    secret_key: SphincsSecretKey {
                        variant,
                        bytes: sk.as_bytes().to_vec(),
                    },
                }
            }
            SphincsVariant::Shake192s => {
                let (pk, sk) = sphincs192s::keypair();
                Self {
                    variant,
                    public_key: SphincsPublicKey {
                        variant,
                        bytes: pk.as_bytes().to_vec(),
                    },
                    secret_key: SphincsSecretKey {
                        variant,
                        bytes: sk.as_bytes().to_vec(),
                    },
                }
            }
            SphincsVariant::Shake256s => {
                let (pk, sk) = sphincs256s::keypair();
                Self {
                    variant,
                    public_key: SphincsPublicKey {
                        variant,
                        bytes: pk.as_bytes().to_vec(),
                    },
                    secret_key: SphincsSecretKey {
                        variant,
                        bytes: sk.as_bytes().to_vec(),
                    },
                }
            }
        }
    }

    /// Creates a keypair from existing keys.
    pub fn from_keys(
        public_key: SphincsPublicKey,
        secret_key: SphincsSecretKey,
    ) -> Result<Self, SphincsError> {
        if public_key.variant != secret_key.variant {
            return Err(SphincsError::VariantMismatch);
        }
        Ok(Self {
            variant: public_key.variant,
            public_key,
            secret_key,
        })
    }

    /// Signs a message.
    pub fn sign(&self, message: &[u8]) -> SphincsSignature {
        let bytes = match self.variant {
            SphincsVariant::Shake128s => {
                let sk = sphincs128s::SecretKey::from_bytes(self.secret_key.as_bytes())
                    .expect("valid secret key");
                let sig = sphincs128s::detached_sign(message, &sk);
                sig.as_bytes().to_vec()
            }
            SphincsVariant::Shake192s => {
                let sk = sphincs192s::SecretKey::from_bytes(self.secret_key.as_bytes())
                    .expect("valid secret key");
                let sig = sphincs192s::detached_sign(message, &sk);
                sig.as_bytes().to_vec()
            }
            SphincsVariant::Shake256s => {
                let sk = sphincs256s::SecretKey::from_bytes(self.secret_key.as_bytes())
                    .expect("valid secret key");
                let sig = sphincs256s::detached_sign(message, &sk);
                sig.as_bytes().to_vec()
            }
        };

        SphincsSignature {
            variant: self.variant,
            bytes,
        }
    }

    /// Returns the public key.
    pub fn public_key(&self) -> &SphincsPublicKey {
        &self.public_key
    }

    /// Returns the secret key.
    pub fn secret_key(&self) -> &SphincsSecretKey {
        &self.secret_key
    }

    /// Returns the variant used.
    pub fn variant(&self) -> SphincsVariant {
        self.variant
    }
}

impl std::fmt::Debug for SphincsKeypair {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("SphincsKeypair")
            .field("variant", &self.variant)
            .field("public_key", &self.public_key)
            .finish()
    }
}

/// Errors from SPHINCS+ operations.
#[derive(Debug, Error)]
pub enum SphincsError {
    #[error("Invalid key size: expected {expected}, got {got}")]
    InvalidKeySize { expected: usize, got: usize },

    #[error("Invalid signature size: expected {expected}, got {got}")]
    InvalidSignatureSize { expected: usize, got: usize },

    #[error("Invalid public key")]
    InvalidPublicKey,

    #[error("Invalid secret key")]
    InvalidSecretKey,

    #[error("Invalid signature")]
    InvalidSignature,

    #[error("Signature verification failed")]
    VerificationFailed,

    #[error("Variant mismatch between key and signature")]
    VariantMismatch,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_generate_128s() {
        let keypair = SphincsKeypair::generate(SphincsVariant::Shake128s);
        assert_eq!(keypair.variant(), SphincsVariant::Shake128s);
        assert_eq!(keypair.public_key().as_bytes().len(), 32);
    }

    #[test]
    fn test_sign_verify_128s() {
        let keypair = SphincsKeypair::generate(SphincsVariant::Shake128s);
        let message = b"Test message for SPHINCS+ signing";

        let signature = keypair.sign(message);
        assert_eq!(signature.len(), 7856);

        // Verify should succeed
        assert!(keypair.public_key().verify(message, &signature).is_ok());
    }

    #[test]
    fn test_sign_verify_192s() {
        let keypair = SphincsKeypair::generate(SphincsVariant::Shake192s);
        let message = b"Test message for SPHINCS+ 192";

        let signature = keypair.sign(message);
        assert_eq!(signature.len(), 16224);

        assert!(keypair.public_key().verify(message, &signature).is_ok());
    }

    #[test]
    fn test_sign_verify_256s() {
        let keypair = SphincsKeypair::generate(SphincsVariant::Shake256s);
        let message = b"Test message for SPHINCS+ 256";

        let signature = keypair.sign(message);
        assert_eq!(signature.len(), 29792);

        assert!(keypair.public_key().verify(message, &signature).is_ok());
    }

    #[test]
    fn test_invalid_signature_fails() {
        let keypair = SphincsKeypair::generate(SphincsVariant::Shake128s);
        let message = b"Original message";
        let signature = keypair.sign(message);

        // Verify with wrong message should fail
        let wrong_message = b"Wrong message";
        assert!(keypair.public_key().verify(wrong_message, &signature).is_err());
    }

    #[test]
    fn test_variant_sizes() {
        assert_eq!(SphincsVariant::Shake128s.signature_size(), 7856);
        assert_eq!(SphincsVariant::Shake128s.public_key_size(), 32);
        assert_eq!(SphincsVariant::Shake128s.secret_key_size(), 64);

        assert_eq!(SphincsVariant::Shake192s.signature_size(), 16224);
        assert_eq!(SphincsVariant::Shake192s.public_key_size(), 48);

        assert_eq!(SphincsVariant::Shake256s.signature_size(), 29792);
        assert_eq!(SphincsVariant::Shake256s.public_key_size(), 64);
    }

    #[test]
    fn test_security_levels() {
        assert_eq!(SphincsVariant::Shake128s.security_level(), 128);
        assert_eq!(SphincsVariant::Shake192s.security_level(), 192);
        assert_eq!(SphincsVariant::Shake256s.security_level(), 256);
    }
}
