//! Signature types for Synor blockchain.
//!
//! All signatures in Synor use the hybrid Ed25519+Dilithium3 scheme for
//! quantum-resistant security. Both components must be valid for a signature
//! to be considered valid.

use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use thiserror::Error;

/// Ed25519 signature size in bytes.
pub const ED25519_SIGNATURE_SIZE: usize = 64;

/// A hybrid signature combining Ed25519 and Dilithium3.
///
/// Both signatures must be valid for the overall signature to be valid.
/// This provides security against both classical and quantum attacks:
/// - Ed25519: Fast, compact, proven security against classical computers
/// - Dilithium3: NIST-standardized, quantum-resistant lattice-based signature
///
/// An attacker must break BOTH algorithms to forge a signature.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub struct HybridSignature {
    /// Ed25519 signature (64 bytes).
    pub ed25519_signature: Vec<u8>,
    /// Dilithium3 signature (variable length, ~3293 bytes).
    pub dilithium_signature: Vec<u8>,
}

impl HybridSignature {
    /// Creates a new hybrid signature from components.
    pub fn new(ed25519: [u8; 64], dilithium: Vec<u8>) -> Self {
        HybridSignature {
            ed25519_signature: ed25519.to_vec(),
            dilithium_signature: dilithium,
        }
    }

    /// Creates a new hybrid signature from Vec components.
    pub fn from_vecs(ed25519: Vec<u8>, dilithium: Vec<u8>) -> Self {
        HybridSignature {
            ed25519_signature: ed25519,
            dilithium_signature: dilithium,
        }
    }

    /// Returns the total size of the signature in bytes.
    pub fn size(&self) -> usize {
        self.ed25519_signature.len() + self.dilithium_signature.len()
    }

    /// Returns the Ed25519 signature component.
    pub fn ed25519(&self) -> &[u8] {
        &self.ed25519_signature
    }

    /// Returns the Dilithium signature component.
    pub fn dilithium(&self) -> &[u8] {
        &self.dilithium_signature
    }

    /// Returns the Ed25519 component bytes (for compatibility).
    pub fn ed25519_bytes(&self) -> &[u8] {
        &self.ed25519_signature
    }

    /// Returns the Ed25519 component as a fixed-size array if valid.
    pub fn ed25519_array(&self) -> Option<[u8; 64]> {
        if self.ed25519_signature.len() != 64 {
            return None;
        }
        let mut arr = [0u8; 64];
        arr.copy_from_slice(&self.ed25519_signature);
        Some(arr)
    }

    /// Returns the Dilithium component bytes.
    pub fn dilithium_bytes(&self) -> &[u8] {
        &self.dilithium_signature
    }

    /// Serializes the signature to bytes.
    pub fn to_bytes(&self) -> Vec<u8> {
        let mut bytes = Vec::with_capacity(self.size());
        bytes.extend_from_slice(&self.ed25519_signature);
        bytes.extend_from_slice(&self.dilithium_signature);
        bytes
    }

    /// Deserializes a signature from bytes.
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, SignatureError> {
        if bytes.len() < ED25519_SIGNATURE_SIZE {
            return Err(SignatureError::InvalidLength {
                expected: ED25519_SIGNATURE_SIZE,
                got: bytes.len(),
            });
        }

        let ed25519 = bytes[..ED25519_SIGNATURE_SIZE].to_vec();
        let dilithium = bytes[ED25519_SIGNATURE_SIZE..].to_vec();

        // Validate that we have a Dilithium signature
        if dilithium.is_empty() {
            return Err(SignatureError::MissingComponent(
                "Dilithium signature required for hybrid signature".to_string(),
            ));
        }

        Ok(HybridSignature {
            ed25519_signature: ed25519,
            dilithium_signature: dilithium,
        })
    }

    /// Returns true (all signatures are hybrid).
    pub fn is_hybrid(&self) -> bool {
        true
    }
}

impl std::fmt::Debug for HybridSignature {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let ed_preview = if self.ed25519_signature.len() >= 8 {
            hex::encode(&self.ed25519_signature[..8])
        } else {
            hex::encode(&self.ed25519_signature)
        };
        f.debug_struct("HybridSignature")
            .field("ed25519", &ed_preview)
            .field("dilithium_len", &self.dilithium_signature.len())
            .finish()
    }
}

impl std::fmt::Display for HybridSignature {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let ed_hex = if self.ed25519_signature.len() >= 8 {
            hex::encode(&self.ed25519_signature[..8])
        } else {
            hex::encode(&self.ed25519_signature)
        };
        write!(f, "HybridSig({}...)", ed_hex)
    }
}

/// Type alias for signature - all Synor signatures are hybrid.
///
/// This alias exists for API clarity and future compatibility.
/// All signatures must contain both Ed25519 and Dilithium components.
pub type Signature = HybridSignature;

/// Errors related to signatures.
#[derive(Debug, Error)]
pub enum SignatureError {
    #[error("Invalid signature length: expected at least {expected}, got {got}")]
    InvalidLength { expected: usize, got: usize },

    #[error("Invalid signature format")]
    InvalidFormat,

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

    #[error("Missing required component: {0}")]
    MissingComponent(String),

    #[error("Ed25519 verification failed")]
    Ed25519VerificationFailed,

    #[error("Dilithium verification failed")]
    DilithiumVerificationFailed,
}

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

    #[test]
    fn test_hybrid_signature_roundtrip() {
        let sig = HybridSignature::new([42u8; 64], vec![1, 2, 3, 4, 5]);

        let bytes = sig.to_bytes();
        let recovered = HybridSignature::from_bytes(&bytes).unwrap();

        assert_eq!(sig.ed25519_signature, recovered.ed25519_signature);
        assert_eq!(sig.dilithium_signature, recovered.dilithium_signature);
    }

    #[test]
    fn test_signature_borsh() {
        let sig = HybridSignature::new([1u8; 64], vec![2u8; 100]);
        let serialized = borsh::to_vec(&sig).unwrap();
        let deserialized: HybridSignature = borsh::from_slice(&serialized).unwrap();

        assert_eq!(sig.ed25519_bytes(), deserialized.ed25519_bytes());
        assert_eq!(sig.dilithium_bytes(), deserialized.dilithium_bytes());
    }

    #[test]
    fn test_signature_size() {
        let sig = HybridSignature::new([0u8; 64], vec![0u8; 3293]);
        assert_eq!(sig.size(), 64 + 3293);
    }

    #[test]
    fn test_signature_is_always_hybrid() {
        let sig = HybridSignature::new([0u8; 64], vec![0u8; 100]);
        assert!(sig.is_hybrid());
    }

    #[test]
    fn test_missing_dilithium_fails() {
        // Only Ed25519 bytes, no Dilithium
        let bytes = [0u8; 64];
        let result = HybridSignature::from_bytes(&bytes);
        assert!(result.is_err());
    }

    #[test]
    fn test_type_alias() {
        // Signature is now a type alias for HybridSignature
        let sig: Signature = HybridSignature::new([1u8; 64], vec![2u8; 50]);
        assert_eq!(sig.size(), 64 + 50);
    }
}
