//! Pairing engine trait for curve abstraction.

use std::fmt::Debug;
use std::hash::Hash;

use ff::PrimeField;
use group::Group;

/// Trait for pairing-friendly elliptic curves.
///
/// This trait abstracts over different pairing-based curves (e.g., BN254)
/// to allow generic implementations of proof aggregation.
///
/// # Example
///
/// ```rust,ignore
/// use samaharam::traits::PairingEngine;
///
/// fn aggregate<E: PairingEngine>(proofs: Vec<Proof<E>>) -> AggregatedProof<E> {
///     // Works with any pairing-friendly curve
/// }
/// ```
pub trait PairingEngine: Clone + Debug + Send + Sync + 'static {
    /// Scalar field element type.
    type Fr: PrimeField + Hash;

    /// G1 affine point type.
    type G1Affine: Clone
        + Debug
        + Send
        + Sync
        + PartialEq
        + Eq
        + Into<Self::G1>
        + group::GroupEncoding
        + group::prime::PrimeCurveAffine;

    /// G1 projective point type.
    type G1: Group<Scalar = Self::Fr>
        + Clone
        + Debug
        + Send
        + Sync
        + group::Curve<AffineRepr = Self::G1Affine>;

    /// G2 affine point type.
    type G2Affine: Clone + Debug + Send + Sync + PartialEq + Eq + group::GroupEncoding + group::prime::PrimeCurveAffine;

    /// Pairing output type (target group).
    type Gt: Group + Clone + Debug + PartialEq + Eq;

    /// Compute the pairing e(P, Q).
    ///
    /// # Arguments
    ///
    /// * `p` - A point in G1
    /// * `q` - A point in G2
    ///
    /// # Returns
    ///
    /// The pairing output in Gt
    fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt;

    /// Compute multi-pairing for batch verification.
    ///
    /// This is more efficient than computing individual pairings
    /// and multiplying, due to shared final exponentiation.
    ///
    /// # Arguments
    ///
    /// * `pairs` - Iterator of (G1, G2) point pairs
    ///
    /// # Returns
    ///
    /// The product of all pairings: ∏ e(Pᵢ, Qᵢ)
    fn multi_pairing<'a>(
        pairs: impl IntoIterator<Item = (&'a Self::G1Affine, &'a Self::G2Affine)>,
    ) -> Self::Gt
    where
        Self::G1Affine: 'a,
        Self::G2Affine: 'a;
}

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

    // TDD: RED - These tests define expected behavior before implementation

    /// Test that PairingEngine can be used as a trait bound
    #[test]
    fn engine_is_object_safe_for_static_dispatch() {
        // This test passes at compile time - PairingEngine works with generics
        fn _accepts_engine<E: PairingEngine>() {}

        // Will fail to compile until Bn254 implements PairingEngine
        // _accepts_engine::<crate::backend::bn254::Bn254>();
    }

    /// Test that associated types have required bounds
    #[test]
    fn associated_types_have_required_bounds() {
        fn _check_fr_bounds<F: PrimeField + Hash>() {}
        fn _check_group_bounds<G: Group + Clone + Debug + Send + Sync>() {}

        // These are compile-time checks
        // Implementation will validate these bounds are met
    }
}
