use ed25519_dalek::{Signer, Verifier};
use serde::{Deserialize, Serialize};

/// Public key wrapper
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct PublicKey(pub [u8; 32]);

/// Secret key wrapper
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecretKey(pub [u8; 32]);

/// Key pair for signing
#[derive(Debug, Clone)]
pub struct KeyPair {
    pub public: PublicKey,
    pub secret: SecretKey,
    signing_key: ed25519_dalek::SigningKey,
}

impl KeyPair {
    /// Generate new random keypair
    pub fn generate() -> Self {
        use rand::RngCore;
        let mut rng = rand::thread_rng();
        let mut secret_bytes = [0u8; 32];
        rng.fill_bytes(&mut secret_bytes);

        let signing_key = ed25519_dalek::SigningKey::from_bytes(&secret_bytes);
        let verifying_key = signing_key.verifying_key();

        Self {
            public: PublicKey(verifying_key.to_bytes()),
            secret: SecretKey(signing_key.to_bytes()),
            signing_key,
        }
    }

    /// Sign a message
    pub fn sign(&self, message: &[u8]) -> [u8; 64] {
        self.signing_key.sign(message).to_bytes()
    }

    /// Verify a signature
    pub fn verify(public_key: &PublicKey, message: &[u8], signature: &[u8; 64]) -> bool {
        let verifying_key = match ed25519_dalek::VerifyingKey::from_bytes(&public_key.0) {
            Ok(key) => key,
            Err(_) => return false,
        };

        let sig = ed25519_dalek::Signature::from_bytes(signature);

        verifying_key.verify(message, &sig).is_ok()
    }
}

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

    #[test]
    fn test_keypair_generation() {
        let keypair = KeyPair::generate();
        assert_ne!(keypair.public.0, [0u8; 32]);
        assert_ne!(keypair.secret.0, [0u8; 32]);
    }

    #[test]
    fn test_sign_verify() {
        let keypair = KeyPair::generate();
        let message = b"Hello, OnionCoin!";

        let signature = keypair.sign(message);
        assert!(KeyPair::verify(&keypair.public, message, &signature));
    }

    #[test]
    fn test_verify_invalid_signature() {
        let keypair = KeyPair::generate();
        let message = b"Hello, OnionCoin!";
        let wrong_sig = [0u8; 64];

        assert!(!KeyPair::verify(&keypair.public, message, &wrong_sig));
    }
}
