//! ZK Proof generation and verification.
//!
//! This module provides the cryptographic proof system for ZK-rollups,
//! supporting multiple proving backends (Groth16, PLONK, etc.).
//!
//! # Proof Lifecycle
//!
//! ```text
//! Setup Phase (one-time):
//!   Circuit → Setup → (ProvingKey, VerificationKey)
//!
//! Proving Phase (per batch):
//!   (Circuit, Witness, ProvingKey) → Prover → Proof
//!
//! Verification Phase (on-chain):
//!   (Proof, PublicInputs, VerificationKey) → Verifier → Accept/Reject
//! ```

use ark_bn254::{Bn254, Fr as ScalarField};
use ark_ff::{BigInteger, PrimeField};
use ark_groth16::ProvingKey as Groth16ProvingKey;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::circuit::{Circuit, CircuitError};

/// Proof system backend selection.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Default)]
pub enum ProofSystemBackend {
    /// Groth16 - smallest proofs (~200 bytes), requires per-circuit trusted setup
    #[default]
    Groth16,
    /// PLONK - universal setup, medium proofs (~500 bytes)
    Plonk,
    /// STARK - no trusted setup, large proofs (~50KB), fast prover
    Stark,
}


/// Verification key for proof validation.
#[derive(Clone)]
pub struct VerificationKey {
    /// Backend type
    pub backend: ProofSystemBackend,
    /// Serialized key data
    pub data: Vec<u8>,
}

impl VerificationKey {
    /// Creates a verification key from Groth16 verification key.
    pub fn from_groth16(vk: &ark_groth16::VerifyingKey<Bn254>) -> Result<Self, ProofError> {
        let mut data = Vec::new();
        vk.serialize_compressed(&mut data)
            .map_err(|e| ProofError::SerializationError(e.to_string()))?;

        Ok(Self {
            backend: ProofSystemBackend::Groth16,
            data,
        })
    }

    /// Deserializes the Groth16 verification key.
    pub fn to_groth16(&self) -> Result<ark_groth16::VerifyingKey<Bn254>, ProofError> {
        if self.backend != ProofSystemBackend::Groth16 {
            return Err(ProofError::BackendMismatch);
        }

        ark_groth16::VerifyingKey::deserialize_compressed(&self.data[..])
            .map_err(|e| ProofError::DeserializationError(e.to_string()))
    }

    /// Returns the key size in bytes.
    pub fn size(&self) -> usize {
        self.data.len()
    }

    /// Serializes to bytes.
    pub fn to_bytes(&self) -> Vec<u8> {
        let mut result = vec![self.backend as u8];
        result.extend(&self.data);
        result
    }

    /// Deserializes from bytes.
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, ProofError> {
        if bytes.is_empty() {
            return Err(ProofError::InvalidKeyFormat);
        }

        let backend = match bytes[0] {
            0 => ProofSystemBackend::Groth16,
            1 => ProofSystemBackend::Plonk,
            2 => ProofSystemBackend::Stark,
            _ => return Err(ProofError::InvalidKeyFormat),
        };

        Ok(Self {
            backend,
            data: bytes[1..].to_vec(),
        })
    }
}

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

/// Proving key for proof generation.
pub struct ProvingKey {
    /// Backend type
    pub backend: ProofSystemBackend,
    /// Serialized key data
    pub data: Vec<u8>,
}

impl ProvingKey {
    /// Creates a proving key from Groth16 proving key.
    pub fn from_groth16(pk: &Groth16ProvingKey<Bn254>) -> Result<Self, ProofError> {
        let mut data = Vec::new();
        pk.serialize_compressed(&mut data)
            .map_err(|e| ProofError::SerializationError(e.to_string()))?;

        Ok(Self {
            backend: ProofSystemBackend::Groth16,
            data,
        })
    }

    /// Deserializes the Groth16 proving key.
    pub fn to_groth16(&self) -> Result<Groth16ProvingKey<Bn254>, ProofError> {
        if self.backend != ProofSystemBackend::Groth16 {
            return Err(ProofError::BackendMismatch);
        }

        Groth16ProvingKey::deserialize_compressed(&self.data[..])
            .map_err(|e| ProofError::DeserializationError(e.to_string()))
    }

    /// Returns the key size in bytes.
    pub fn size(&self) -> usize {
        self.data.len()
    }
}

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

/// Zero-knowledge proof.
#[derive(Clone, Serialize, Deserialize)]
pub struct Proof {
    /// Backend used to generate this proof
    pub backend: ProofSystemBackend,
    /// Serialized proof data
    pub data: Vec<u8>,
    /// Public inputs (for verification)
    pub public_inputs: Vec<[u8; 32]>,
}

impl Proof {
    /// Creates a proof from Groth16 proof.
    pub fn from_groth16(
        proof: &ark_groth16::Proof<Bn254>,
        public_inputs: Vec<ScalarField>,
    ) -> Result<Self, ProofError> {
        let mut data = Vec::new();
        proof
            .serialize_compressed(&mut data)
            .map_err(|e| ProofError::SerializationError(e.to_string()))?;

        // Convert field elements to bytes
        let public_inputs_bytes: Vec<[u8; 32]> = public_inputs
            .iter()
            .map(|f| {
                let mut bytes = [0u8; 32];
                let f_bytes = f.into_bigint().to_bytes_le();
                bytes[..f_bytes.len().min(32)].copy_from_slice(&f_bytes[..f_bytes.len().min(32)]);
                bytes
            })
            .collect();

        Ok(Self {
            backend: ProofSystemBackend::Groth16,
            data,
            public_inputs: public_inputs_bytes,
        })
    }

    /// Deserializes the Groth16 proof.
    pub fn to_groth16(&self) -> Result<ark_groth16::Proof<Bn254>, ProofError> {
        if self.backend != ProofSystemBackend::Groth16 {
            return Err(ProofError::BackendMismatch);
        }

        ark_groth16::Proof::deserialize_compressed(&self.data[..])
            .map_err(|e| ProofError::DeserializationError(e.to_string()))
    }

    /// Returns the proof size in bytes.
    pub fn size(&self) -> usize {
        self.data.len()
    }

    /// Serializes to bytes.
    pub fn to_bytes(&self) -> Vec<u8> {
        let mut result = vec![self.backend as u8];

        // Write data length and data
        let data_len = (self.data.len() as u32).to_le_bytes();
        result.extend(&data_len);
        result.extend(&self.data);

        // Write public inputs
        let input_count = (self.public_inputs.len() as u32).to_le_bytes();
        result.extend(&input_count);
        for input in &self.public_inputs {
            result.extend(input);
        }

        result
    }

    /// Deserializes from bytes.
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, ProofError> {
        if bytes.len() < 9 {
            return Err(ProofError::InvalidProofFormat);
        }

        let backend = match bytes[0] {
            0 => ProofSystemBackend::Groth16,
            1 => ProofSystemBackend::Plonk,
            2 => ProofSystemBackend::Stark,
            _ => return Err(ProofError::InvalidProofFormat),
        };

        let data_len = u32::from_le_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]) as usize;
        if bytes.len() < 5 + data_len + 4 {
            return Err(ProofError::InvalidProofFormat);
        }

        let data = bytes[5..5 + data_len].to_vec();

        let input_offset = 5 + data_len;
        let input_count =
            u32::from_le_bytes([bytes[input_offset], bytes[input_offset + 1], bytes[input_offset + 2], bytes[input_offset + 3]])
                as usize;

        let mut public_inputs = Vec::with_capacity(input_count);
        let mut offset = input_offset + 4;
        for _ in 0..input_count {
            if bytes.len() < offset + 32 {
                return Err(ProofError::InvalidProofFormat);
            }
            let mut input = [0u8; 32];
            input.copy_from_slice(&bytes[offset..offset + 32]);
            public_inputs.push(input);
            offset += 32;
        }

        Ok(Self {
            backend,
            data,
            public_inputs,
        })
    }
}

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

/// Proof system for generating and verifying proofs.
pub struct ProofSystem {
    backend: ProofSystemBackend,
}

impl ProofSystem {
    /// Creates a new proof system with the specified backend.
    pub fn new(backend: ProofSystemBackend) -> Self {
        Self { backend }
    }

    /// Creates a proof system with the default backend (Groth16).
    pub fn groth16() -> Self {
        Self::new(ProofSystemBackend::Groth16)
    }

    /// Returns the backend used.
    pub fn backend(&self) -> ProofSystemBackend {
        self.backend
    }

    /// Generates proving and verification keys for a circuit.
    ///
    /// This is the "trusted setup" phase for Groth16.
    /// For production, use a multi-party computation ceremony.
    pub fn setup<C: Circuit>(&self, _circuit: &C) -> Result<(ProvingKey, VerificationKey), ProofError> {
        match self.backend {
            ProofSystemBackend::Groth16 => {
                // In a real implementation, we would:
                // 1. Convert our Circuit trait to ark_relations::r1cs::ConstraintSynthesizer
                // 2. Run Groth16::circuit_specific_setup

                // For now, return placeholder keys
                // Real implementation would use:
                // let (pk, vk) = Groth16::<Bn254>::circuit_specific_setup(circuit, rng)?;

                Ok((
                    ProvingKey {
                        backend: self.backend,
                        data: vec![0u8; 1024], // Placeholder
                    },
                    VerificationKey {
                        backend: self.backend,
                        data: vec![0u8; 256], // Placeholder
                    },
                ))
            }
            ProofSystemBackend::Plonk => {
                Err(ProofError::UnsupportedBackend("PLONK not yet implemented".into()))
            }
            ProofSystemBackend::Stark => {
                Err(ProofError::UnsupportedBackend("STARK not yet implemented".into()))
            }
        }
    }

    /// Generates a proof for a circuit with the given proving key.
    pub fn prove<C: Circuit>(
        &self,
        _circuit: &C,
        _proving_key: &ProvingKey,
    ) -> Result<Proof, ProofError> {
        match self.backend {
            ProofSystemBackend::Groth16 => {
                // In a real implementation:
                // let proof = Groth16::<Bn254>::prove(&pk, circuit, rng)?;

                Ok(Proof {
                    backend: self.backend,
                    data: vec![0u8; 192], // Groth16 proof is ~192 bytes
                    public_inputs: Vec::new(),
                })
            }
            _ => Err(ProofError::UnsupportedBackend(format!("{:?}", self.backend))),
        }
    }

    /// Verifies a proof against a verification key.
    pub fn verify(
        &self,
        _proof: &Proof,
        _verification_key: &VerificationKey,
    ) -> Result<bool, ProofError> {
        match self.backend {
            ProofSystemBackend::Groth16 => {
                // In a real implementation:
                // let pvk = PreparedVerifyingKey::from(&vk);
                // let result = Groth16::<Bn254>::verify_with_processed_vk(&pvk, &public_inputs, &proof)?;

                // Placeholder: always return true for testing
                Ok(true)
            }
            _ => Err(ProofError::UnsupportedBackend(format!("{:?}", self.backend))),
        }
    }

    /// Estimates proof generation time for a circuit.
    pub fn estimate_proving_time<C: Circuit>(circuit: &C) -> std::time::Duration {
        // Rough estimates based on circuit complexity
        let config = circuit.config();
        let base_ms = 100; // Base overhead
        let per_constraint_us = 10; // Microseconds per constraint

        // Estimate constraints based on batch size
        let estimated_constraints = config.max_batch_size * 1000;

        std::time::Duration::from_millis(
            base_ms + (estimated_constraints as u64 * per_constraint_us / 1000),
        )
    }

    /// Estimates proof verification time.
    pub fn estimate_verification_time(backend: ProofSystemBackend) -> std::time::Duration {
        match backend {
            ProofSystemBackend::Groth16 => std::time::Duration::from_millis(10),
            ProofSystemBackend::Plonk => std::time::Duration::from_millis(15),
            ProofSystemBackend::Stark => std::time::Duration::from_millis(30),
        }
    }

    /// Returns the expected proof size for a backend.
    pub fn proof_size(backend: ProofSystemBackend) -> usize {
        match backend {
            ProofSystemBackend::Groth16 => 192,      // ~200 bytes
            ProofSystemBackend::Plonk => 512,        // ~500 bytes
            ProofSystemBackend::Stark => 50 * 1024, // ~50KB
        }
    }
}

impl Default for ProofSystem {
    fn default() -> Self {
        Self::groth16()
    }
}

/// Proof system errors.
#[derive(Debug, Error)]
pub enum ProofError {
    #[error("Proof generation failed: {0}")]
    ProvingError(String),

    #[error("Proof verification failed: {0}")]
    VerificationError(String),

    #[error("Setup failed: {0}")]
    SetupError(String),

    #[error("Serialization error: {0}")]
    SerializationError(String),

    #[error("Deserialization error: {0}")]
    DeserializationError(String),

    #[error("Backend mismatch")]
    BackendMismatch,

    #[error("Invalid proof format")]
    InvalidProofFormat,

    #[error("Invalid key format")]
    InvalidKeyFormat,

    #[error("Unsupported backend: {0}")]
    UnsupportedBackend(String),

    #[error("Circuit error: {0}")]
    CircuitError(#[from] CircuitError),
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::circuit::{BatchCircuit, TransferCircuit};
    use crate::state::StateRoot;

    // ============================================================================
    // ProofSystemBackend Tests
    // ============================================================================

    #[test]
    fn test_proof_system_backend_default() {
        let backend = ProofSystemBackend::default();
        assert_eq!(backend, ProofSystemBackend::Groth16);
    }

    #[test]
    fn test_proof_system_backend_variants() {
        assert_eq!(ProofSystemBackend::Groth16 as u8, 0);
        assert_eq!(ProofSystemBackend::Plonk as u8, 1);
        assert_eq!(ProofSystemBackend::Stark as u8, 2);
    }

    #[test]
    fn test_proof_system_backend_clone() {
        let backend = ProofSystemBackend::Groth16;
        let cloned = backend;
        assert_eq!(backend, cloned);
    }

    #[test]
    fn test_proof_system_backend_debug() {
        let backend = ProofSystemBackend::Groth16;
        let debug_str = format!("{:?}", backend);
        assert!(debug_str.contains("Groth16"));
    }

    // ============================================================================
    // ProofSystem Tests
    // ============================================================================

    #[test]
    fn test_proof_system_creation() {
        let ps = ProofSystem::groth16();
        assert_eq!(ps.backend(), ProofSystemBackend::Groth16);
    }

    #[test]
    fn test_proof_system_new_with_backends() {
        let groth16 = ProofSystem::new(ProofSystemBackend::Groth16);
        assert_eq!(groth16.backend(), ProofSystemBackend::Groth16);

        let plonk = ProofSystem::new(ProofSystemBackend::Plonk);
        assert_eq!(plonk.backend(), ProofSystemBackend::Plonk);

        let stark = ProofSystem::new(ProofSystemBackend::Stark);
        assert_eq!(stark.backend(), ProofSystemBackend::Stark);
    }

    #[test]
    fn test_proof_system_default() {
        let ps = ProofSystem::default();
        assert_eq!(ps.backend(), ProofSystemBackend::Groth16);
    }

    #[test]
    fn test_proof_system_setup_groth16() {
        let ps = ProofSystem::groth16();
        let circuit = BatchCircuit::new(StateRoot::zero(), StateRoot::zero());

        let result = ps.setup(&circuit);
        assert!(result.is_ok());

        let (pk, vk) = result.unwrap();
        assert_eq!(pk.backend, ProofSystemBackend::Groth16);
        assert_eq!(vk.backend, ProofSystemBackend::Groth16);
    }

    #[test]
    fn test_proof_system_setup_plonk_unsupported() {
        let ps = ProofSystem::new(ProofSystemBackend::Plonk);
        let circuit = BatchCircuit::new(StateRoot::zero(), StateRoot::zero());

        let result = ps.setup(&circuit);
        assert!(matches!(result, Err(ProofError::UnsupportedBackend(_))));
    }

    #[test]
    fn test_proof_system_setup_stark_unsupported() {
        let ps = ProofSystem::new(ProofSystemBackend::Stark);
        let circuit = BatchCircuit::new(StateRoot::zero(), StateRoot::zero());

        let result = ps.setup(&circuit);
        assert!(matches!(result, Err(ProofError::UnsupportedBackend(_))));
    }

    #[test]
    fn test_proof_system_prove_groth16() {
        let ps = ProofSystem::groth16();
        let circuit = TransferCircuit::new(
            StateRoot::zero(),
            StateRoot([1u8; 32]),
            0,
            1,
            100,
        );
        let (pk, _) = ps.setup(&circuit).unwrap();

        let proof = ps.prove(&circuit, &pk);
        assert!(proof.is_ok());
        assert_eq!(proof.unwrap().backend, ProofSystemBackend::Groth16);
    }

    #[test]
    fn test_proof_system_verify_groth16() {
        let ps = ProofSystem::groth16();
        let circuit = TransferCircuit::new(
            StateRoot::zero(),
            StateRoot([1u8; 32]),
            0,
            1,
            100,
        );
        let (pk, vk) = ps.setup(&circuit).unwrap();
        let proof = ps.prove(&circuit, &pk).unwrap();

        let result = ps.verify(&proof, &vk);
        assert!(result.is_ok());
        assert!(result.unwrap());
    }

    #[test]
    fn test_proof_system_prove_unsupported_backend() {
        let ps = ProofSystem::new(ProofSystemBackend::Plonk);
        let circuit = TransferCircuit::new(
            StateRoot::zero(),
            StateRoot([1u8; 32]),
            0,
            1,
            100,
        );
        let pk = ProvingKey {
            backend: ProofSystemBackend::Plonk,
            data: vec![0u8; 100],
        };

        let result = ps.prove(&circuit, &pk);
        assert!(matches!(result, Err(ProofError::UnsupportedBackend(_))));
    }

    #[test]
    fn test_proof_system_verify_unsupported_backend() {
        let ps = ProofSystem::new(ProofSystemBackend::Stark);
        let proof = Proof {
            backend: ProofSystemBackend::Stark,
            data: vec![0u8; 100],
            public_inputs: vec![],
        };
        let vk = VerificationKey {
            backend: ProofSystemBackend::Stark,
            data: vec![0u8; 100],
        };

        let result = ps.verify(&proof, &vk);
        assert!(matches!(result, Err(ProofError::UnsupportedBackend(_))));
    }

    // ============================================================================
    // Proof Size and Time Estimates Tests
    // ============================================================================

    #[test]
    fn test_proof_sizes() {
        assert!(ProofSystem::proof_size(ProofSystemBackend::Groth16) < 300);
        assert!(ProofSystem::proof_size(ProofSystemBackend::Plonk) < 1000);
        assert!(ProofSystem::proof_size(ProofSystemBackend::Stark) > 10000);
    }

    #[test]
    fn test_proof_size_groth16_smallest() {
        let groth16_size = ProofSystem::proof_size(ProofSystemBackend::Groth16);
        let plonk_size = ProofSystem::proof_size(ProofSystemBackend::Plonk);
        let stark_size = ProofSystem::proof_size(ProofSystemBackend::Stark);

        assert!(groth16_size < plonk_size);
        assert!(plonk_size < stark_size);
    }

    #[test]
    fn test_proof_size_exact_values() {
        assert_eq!(ProofSystem::proof_size(ProofSystemBackend::Groth16), 192);
        assert_eq!(ProofSystem::proof_size(ProofSystemBackend::Plonk), 512);
        assert_eq!(ProofSystem::proof_size(ProofSystemBackend::Stark), 51200);
    }

    #[test]
    fn test_verification_time_estimates() {
        let groth16_time = ProofSystem::estimate_verification_time(ProofSystemBackend::Groth16);
        let plonk_time = ProofSystem::estimate_verification_time(ProofSystemBackend::Plonk);
        let stark_time = ProofSystem::estimate_verification_time(ProofSystemBackend::Stark);

        assert!(groth16_time < plonk_time);
        assert!(plonk_time < stark_time);
    }

    #[test]
    fn test_verification_time_exact_values() {
        assert_eq!(
            ProofSystem::estimate_verification_time(ProofSystemBackend::Groth16),
            std::time::Duration::from_millis(10)
        );
        assert_eq!(
            ProofSystem::estimate_verification_time(ProofSystemBackend::Plonk),
            std::time::Duration::from_millis(15)
        );
        assert_eq!(
            ProofSystem::estimate_verification_time(ProofSystemBackend::Stark),
            std::time::Duration::from_millis(30)
        );
    }

    #[test]
    fn test_estimate_proving_time() {
        let circuit = BatchCircuit::new(StateRoot::zero(), StateRoot::zero());
        let time = ProofSystem::estimate_proving_time(&circuit);

        assert!(time.as_millis() > 0);
    }

    // ============================================================================
    // VerificationKey Tests
    // ============================================================================

    #[test]
    fn test_verification_key_creation() {
        let vk = VerificationKey {
            backend: ProofSystemBackend::Groth16,
            data: vec![1, 2, 3, 4, 5],
        };
        assert_eq!(vk.backend, ProofSystemBackend::Groth16);
        assert_eq!(vk.data, vec![1, 2, 3, 4, 5]);
    }

    #[test]
    fn test_verification_key_size() {
        let vk = VerificationKey {
            backend: ProofSystemBackend::Groth16,
            data: vec![0u8; 256],
        };
        assert_eq!(vk.size(), 256);
    }

    #[test]
    fn test_verification_key_serialization() {
        let vk = VerificationKey {
            backend: ProofSystemBackend::Groth16,
            data: vec![1, 2, 3, 4, 5],
        };

        let bytes = vk.to_bytes();
        let decoded = VerificationKey::from_bytes(&bytes).unwrap();

        assert_eq!(decoded.backend, vk.backend);
        assert_eq!(decoded.data, vk.data);
    }

    #[test]
    fn test_verification_key_serialization_all_backends() {
        for backend in [
            ProofSystemBackend::Groth16,
            ProofSystemBackend::Plonk,
            ProofSystemBackend::Stark,
        ] {
            let vk = VerificationKey {
                backend,
                data: vec![0xaa, 0xbb, 0xcc],
            };

            let bytes = vk.to_bytes();
            let decoded = VerificationKey::from_bytes(&bytes).unwrap();

            assert_eq!(decoded.backend, backend);
            assert_eq!(decoded.data, vk.data);
        }
    }

    #[test]
    fn test_verification_key_from_bytes_empty_error() {
        let result = VerificationKey::from_bytes(&[]);
        assert!(matches!(result, Err(ProofError::InvalidKeyFormat)));
    }

    #[test]
    fn test_verification_key_from_bytes_invalid_backend() {
        let result = VerificationKey::from_bytes(&[99, 1, 2, 3]);
        assert!(matches!(result, Err(ProofError::InvalidKeyFormat)));
    }

    #[test]
    fn test_verification_key_debug() {
        let vk = VerificationKey {
            backend: ProofSystemBackend::Groth16,
            data: vec![1, 2, 3],
        };
        let debug_str = format!("{:?}", vk);
        assert!(debug_str.contains("VerificationKey"));
        assert!(debug_str.contains("Groth16"));
        assert!(debug_str.contains("size"));
    }

    #[test]
    fn test_verification_key_clone() {
        let vk = VerificationKey {
            backend: ProofSystemBackend::Groth16,
            data: vec![1, 2, 3, 4, 5],
        };
        let cloned = vk.clone();
        assert_eq!(vk.backend, cloned.backend);
        assert_eq!(vk.data, cloned.data);
    }

    #[test]
    fn test_verification_key_empty_data() {
        let vk = VerificationKey {
            backend: ProofSystemBackend::Groth16,
            data: vec![],
        };
        assert_eq!(vk.size(), 0);

        let bytes = vk.to_bytes();
        assert_eq!(bytes.len(), 1);
    }

    #[test]
    fn test_verification_key_large_data() {
        let vk = VerificationKey {
            backend: ProofSystemBackend::Groth16,
            data: vec![0xffu8; 10000],
        };
        assert_eq!(vk.size(), 10000);

        let bytes = vk.to_bytes();
        let decoded = VerificationKey::from_bytes(&bytes).unwrap();
        assert_eq!(decoded.data.len(), 10000);
    }

    #[test]
    fn test_verification_key_to_groth16_wrong_backend() {
        let vk = VerificationKey {
            backend: ProofSystemBackend::Plonk,
            data: vec![1, 2, 3],
        };
        let result = vk.to_groth16();
        assert!(matches!(result, Err(ProofError::BackendMismatch)));
    }

    // ============================================================================
    // ProvingKey Tests
    // ============================================================================

    #[test]
    fn test_proving_key_creation() {
        let pk = ProvingKey {
            backend: ProofSystemBackend::Groth16,
            data: vec![1, 2, 3, 4, 5],
        };
        assert_eq!(pk.backend, ProofSystemBackend::Groth16);
        assert_eq!(pk.data, vec![1, 2, 3, 4, 5]);
    }

    #[test]
    fn test_proving_key_size() {
        let pk = ProvingKey {
            backend: ProofSystemBackend::Groth16,
            data: vec![0u8; 1024],
        };
        assert_eq!(pk.size(), 1024);
    }

    #[test]
    fn test_proving_key_debug() {
        let pk = ProvingKey {
            backend: ProofSystemBackend::Groth16,
            data: vec![1, 2, 3],
        };
        let debug_str = format!("{:?}", pk);
        assert!(debug_str.contains("ProvingKey"));
        assert!(debug_str.contains("Groth16"));
        assert!(debug_str.contains("size"));
    }

    #[test]
    fn test_proving_key_to_groth16_wrong_backend() {
        let pk = ProvingKey {
            backend: ProofSystemBackend::Stark,
            data: vec![1, 2, 3],
        };
        let result = pk.to_groth16();
        assert!(matches!(result, Err(ProofError::BackendMismatch)));
    }

    #[test]
    fn test_proving_key_empty_data() {
        let pk = ProvingKey {
            backend: ProofSystemBackend::Groth16,
            data: vec![],
        };
        assert_eq!(pk.size(), 0);
    }

    // ============================================================================
    // Proof Tests
    // ============================================================================

    #[test]
    fn test_proof_creation() {
        let proof = Proof {
            backend: ProofSystemBackend::Groth16,
            data: vec![1, 2, 3, 4, 5],
            public_inputs: vec![[0xab; 32], [0xcd; 32]],
        };
        assert_eq!(proof.backend, ProofSystemBackend::Groth16);
        assert_eq!(proof.data, vec![1, 2, 3, 4, 5]);
        assert_eq!(proof.public_inputs.len(), 2);
    }

    #[test]
    fn test_proof_size() {
        let proof = Proof {
            backend: ProofSystemBackend::Groth16,
            data: vec![0u8; 192],
            public_inputs: vec![],
        };
        assert_eq!(proof.size(), 192);
    }

    #[test]
    fn test_proof_serialization() {
        let proof = Proof {
            backend: ProofSystemBackend::Groth16,
            data: vec![1, 2, 3, 4, 5],
            public_inputs: vec![[0xab; 32], [0xcd; 32]],
        };

        let bytes = proof.to_bytes();
        let decoded = Proof::from_bytes(&bytes).unwrap();

        assert_eq!(decoded.backend, proof.backend);
        assert_eq!(decoded.data, proof.data);
        assert_eq!(decoded.public_inputs, proof.public_inputs);
    }

    #[test]
    fn test_proof_serialization_all_backends() {
        for backend in [
            ProofSystemBackend::Groth16,
            ProofSystemBackend::Plonk,
            ProofSystemBackend::Stark,
        ] {
            let proof = Proof {
                backend,
                data: vec![0xaa, 0xbb, 0xcc],
                public_inputs: vec![[0x11; 32]],
            };

            let bytes = proof.to_bytes();
            let decoded = Proof::from_bytes(&bytes).unwrap();

            assert_eq!(decoded.backend, backend);
            assert_eq!(decoded.data, proof.data);
            assert_eq!(decoded.public_inputs, proof.public_inputs);
        }
    }

    #[test]
    fn test_proof_serialization_empty_inputs() {
        let proof = Proof {
            backend: ProofSystemBackend::Groth16,
            data: vec![1, 2, 3],
            public_inputs: vec![],
        };

        let bytes = proof.to_bytes();
        let decoded = Proof::from_bytes(&bytes).unwrap();

        assert!(decoded.public_inputs.is_empty());
    }

    #[test]
    fn test_proof_serialization_many_inputs() {
        let proof = Proof {
            backend: ProofSystemBackend::Groth16,
            data: vec![1, 2, 3],
            public_inputs: (0..100).map(|i| [i as u8; 32]).collect(),
        };

        let bytes = proof.to_bytes();
        let decoded = Proof::from_bytes(&bytes).unwrap();

        assert_eq!(decoded.public_inputs.len(), 100);
    }

    #[test]
    fn test_proof_from_bytes_too_short() {
        let result = Proof::from_bytes(&[0, 1, 2, 3, 4, 5, 6, 7]);
        assert!(matches!(result, Err(ProofError::InvalidProofFormat)));
    }

    #[test]
    fn test_proof_from_bytes_invalid_backend() {
        let bytes = [99u8, 0, 0, 0, 0, 0, 0, 0, 0];
        let result = Proof::from_bytes(&bytes);
        assert!(matches!(result, Err(ProofError::InvalidProofFormat)));
    }

    #[test]
    fn test_proof_from_bytes_truncated_data() {
        let mut bytes = vec![0u8];
        bytes.extend(&100u32.to_le_bytes());
        bytes.extend(&[1, 2, 3, 4, 5]);

        let result = Proof::from_bytes(&bytes);
        assert!(matches!(result, Err(ProofError::InvalidProofFormat)));
    }

    #[test]
    fn test_proof_debug() {
        let proof = Proof {
            backend: ProofSystemBackend::Groth16,
            data: vec![1, 2, 3],
            public_inputs: vec![[0xab; 32]],
        };
        let debug_str = format!("{:?}", proof);
        assert!(debug_str.contains("Proof"));
        assert!(debug_str.contains("Groth16"));
        assert!(debug_str.contains("public_inputs"));
    }

    #[test]
    fn test_proof_clone() {
        let proof = Proof {
            backend: ProofSystemBackend::Groth16,
            data: vec![1, 2, 3, 4, 5],
            public_inputs: vec![[0xab; 32]],
        };
        let cloned = proof.clone();

        assert_eq!(proof.backend, cloned.backend);
        assert_eq!(proof.data, cloned.data);
        assert_eq!(proof.public_inputs, cloned.public_inputs);
    }

    #[test]
    fn test_proof_to_groth16_wrong_backend() {
        let proof = Proof {
            backend: ProofSystemBackend::Plonk,
            data: vec![1, 2, 3],
            public_inputs: vec![],
        };
        let result = proof.to_groth16();
        assert!(matches!(result, Err(ProofError::BackendMismatch)));
    }

    // ============================================================================
    // ProofError Tests
    // ============================================================================

    #[test]
    fn test_proof_error_proving_display() {
        let error = ProofError::ProvingError("test proving error".to_string());
        let display = format!("{}", error);
        assert!(display.contains("Proof generation failed"));
        assert!(display.contains("test proving error"));
    }

    #[test]
    fn test_proof_error_verification_display() {
        let error = ProofError::VerificationError("verification failed".to_string());
        let display = format!("{}", error);
        assert!(display.contains("Proof verification failed"));
    }

    #[test]
    fn test_proof_error_setup_display() {
        let error = ProofError::SetupError("setup failed".to_string());
        let display = format!("{}", error);
        assert!(display.contains("Setup failed"));
    }

    #[test]
    fn test_proof_error_serialization_display() {
        let error = ProofError::SerializationError("serialization failed".to_string());
        let display = format!("{}", error);
        assert!(display.contains("Serialization error"));
    }

    #[test]
    fn test_proof_error_deserialization_display() {
        let error = ProofError::DeserializationError("deserialization failed".to_string());
        let display = format!("{}", error);
        assert!(display.contains("Deserialization error"));
    }

    #[test]
    fn test_proof_error_backend_mismatch_display() {
        let error = ProofError::BackendMismatch;
        let display = format!("{}", error);
        assert!(display.contains("Backend mismatch"));
    }

    #[test]
    fn test_proof_error_invalid_proof_format_display() {
        let error = ProofError::InvalidProofFormat;
        let display = format!("{}", error);
        assert!(display.contains("Invalid proof format"));
    }

    #[test]
    fn test_proof_error_invalid_key_format_display() {
        let error = ProofError::InvalidKeyFormat;
        let display = format!("{}", error);
        assert!(display.contains("Invalid key format"));
    }

    #[test]
    fn test_proof_error_unsupported_backend_display() {
        let error = ProofError::UnsupportedBackend("STARK".to_string());
        let display = format!("{}", error);
        assert!(display.contains("Unsupported backend"));
        assert!(display.contains("STARK"));
    }

    #[test]
    fn test_proof_error_circuit_error_display() {
        let circuit_error = CircuitError::SynthesisError("test".to_string());
        let error: ProofError = circuit_error.into();
        let display = format!("{}", error);
        assert!(display.contains("Circuit error"));
    }

    #[test]
    fn test_proof_error_debug_format() {
        let error = ProofError::BackendMismatch;
        let debug_str = format!("{:?}", error);
        assert!(debug_str.contains("BackendMismatch"));
    }

    // ============================================================================
    // Integration Tests
    // ============================================================================

    #[test]
    fn test_full_proof_workflow() {
        let ps = ProofSystem::groth16();

        let circuit = TransferCircuit::new(
            StateRoot::zero(),
            StateRoot([1u8; 32]),
            0,
            1,
            100,
        );
        let (pk, vk) = ps.setup(&circuit).unwrap();

        let proof = ps.prove(&circuit, &pk).unwrap();
        assert_eq!(proof.backend, ProofSystemBackend::Groth16);

        let is_valid = ps.verify(&proof, &vk).unwrap();
        assert!(is_valid);
    }

    #[test]
    fn test_proof_roundtrip_serialization() {
        let ps = ProofSystem::groth16();
        let circuit = BatchCircuit::new(StateRoot::zero(), StateRoot([1u8; 32]));
        let (pk, _) = ps.setup(&circuit).unwrap();
        let proof = ps.prove(&circuit, &pk).unwrap();

        let bytes = proof.to_bytes();
        let restored = Proof::from_bytes(&bytes).unwrap();

        assert_eq!(proof.backend, restored.backend);
        assert_eq!(proof.data, restored.data);
    }

    #[test]
    fn test_verification_key_roundtrip() {
        let ps = ProofSystem::groth16();
        let circuit = BatchCircuit::new(StateRoot::zero(), StateRoot([1u8; 32]));
        let (_, vk) = ps.setup(&circuit).unwrap();

        let bytes = vk.to_bytes();
        let restored = VerificationKey::from_bytes(&bytes).unwrap();

        assert_eq!(vk.backend, restored.backend);
        assert_eq!(vk.data, restored.data);
    }

    // ============================================================================
    // Edge Cases
    // ============================================================================

    #[test]
    fn test_proof_with_max_public_inputs() {
        let proof = Proof {
            backend: ProofSystemBackend::Groth16,
            data: vec![0u8; 192],
            public_inputs: (0..1000).map(|_| [0xffu8; 32]).collect(),
        };

        let bytes = proof.to_bytes();
        let decoded = Proof::from_bytes(&bytes).unwrap();

        assert_eq!(decoded.public_inputs.len(), 1000);
    }

    #[test]
    fn test_empty_proof_data() {
        let proof = Proof {
            backend: ProofSystemBackend::Groth16,
            data: vec![],
            public_inputs: vec![],
        };

        let bytes = proof.to_bytes();
        let decoded = Proof::from_bytes(&bytes).unwrap();

        assert!(decoded.data.is_empty());
        assert!(decoded.public_inputs.is_empty());
    }

    #[test]
    fn test_large_proof_data() {
        let proof = Proof {
            backend: ProofSystemBackend::Stark,
            data: vec![0xffu8; 50 * 1024],
            public_inputs: vec![[0xab; 32]],
        };

        let bytes = proof.to_bytes();
        let decoded = Proof::from_bytes(&bytes).unwrap();

        assert_eq!(decoded.data.len(), 50 * 1024);
    }
}
