//! ZK constraint backend for zero-knowledge proof systems.
//!
//! This module provides constraint-emitting variants of BigInt operations
//! for use with zero-knowledge proof systems like zk-SNARKs and zk-STARKs.
//!
//! This module is feature-gated behind the `zk` feature flag.

use crate::error::Result;
use crate::types::BigInt;
use alloc::vec::Vec;
use alloc::vec;

/// ZK BigInt that emits constraints instead of computing values.
///
/// This type represents a BigInt value in a constraint system,
/// where operations emit constraints rather than performing actual computation.
#[derive(Clone, Debug)]
pub struct ZKBigInt {
    /// Variable identifiers in the constraint system.
    pub variables: Vec<usize>,
    /// Number of limbs.
    pub limb_count: usize,
    /// Sign (as a constraint variable).
    pub sign_var: Option<usize>,
}

/// Trait for ZK constraint backends.
///
/// Different proof systems can implement this trait to provide
/// constraint generation for BigInt operations.
pub trait ZKBackend {
    /// Emit a constraint for addition: c = a + b.
    fn emit_add(&mut self, a: &ZKBigInt, b: &ZKBigInt) -> Result<ZKBigInt>;

    /// Emit a constraint for multiplication: c = a × b.
    fn emit_mul(&mut self, a: &ZKBigInt, b: &ZKBigInt) -> Result<ZKBigInt>;

    /// Emit a constraint for equality: a == b.
    fn emit_eq(&mut self, a: &ZKBigInt, b: &ZKBigInt) -> Result<()>;

    /// Create a constant value in the constraint system.
    fn constant(&mut self, value: &BigInt) -> Result<ZKBigInt>;

    /// Create a public input variable.
    fn public_input(&mut self, limb_count: usize) -> Result<ZKBigInt>;

    /// Create a private witness variable.
    fn private_witness(&mut self, limb_count: usize) -> Result<ZKBigInt>;
}

impl ZKBigInt {
    /// Create a new ZKBigInt with the given number of limbs.
    pub fn new(limb_count: usize) -> Self {
        Self {
            variables: vec![0; limb_count],
            limb_count,
            sign_var: None,
        }
    }

    /// Get the number of limbs.
    pub fn limb_count(&self) -> usize {
        self.limb_count
    }
}

/// Placeholder implementation for when ZK feature is not enabled.
#[cfg(not(feature = "zk"))]
pub struct PlaceholderBackend;

#[cfg(not(feature = "zk"))]
impl ZKBackend for PlaceholderBackend {
    fn emit_add(&mut self, _a: &ZKBigInt, _b: &ZKBigInt) -> Result<ZKBigInt> {
        Err(crate::error::BigIntError::Overflow) // ZK feature not enabled
    }

    fn emit_mul(&mut self, _a: &ZKBigInt, _b: &ZKBigInt) -> Result<ZKBigInt> {
        Err(crate::error::BigIntError::Overflow) // ZK feature not enabled
    }

    fn emit_eq(&mut self, _a: &ZKBigInt, _b: &ZKBigInt) -> Result<()> {
        Err(crate::error::BigIntError::Overflow) // ZK feature not enabled
    }

    fn constant(&mut self, _value: &BigInt) -> Result<ZKBigInt> {
        Err(crate::error::BigIntError::Overflow) // ZK feature not enabled
    }

    fn public_input(&mut self, _limb_count: usize) -> Result<ZKBigInt> {
        Err(crate::error::BigIntError::Overflow) // ZK feature not enabled
    }

    fn private_witness(&mut self, _limb_count: usize) -> Result<ZKBigInt> {
        Err(crate::error::BigIntError::Overflow) // ZK feature not enabled
    }
}

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

    #[test]
    fn test_zk_bigint_new() {
        let zk_bi = ZKBigInt::new(4);
        assert_eq!(zk_bi.limb_count(), 4);
    }
}
