//! BN254 curve backend for EVM compatibility.
//!
//! BN254 (alt_bn128) is the curve with EVM precompiles at:
//! - 0x06: ECADD
//! - 0x07: ECMUL  
//! - 0x08: ECPAIRING

use halo2curves::bn256::{Bn256, Fr, G1Affine, G2Affine, Gt, G1};
use halo2curves::pairing::Engine;

use crate::traits::PairingEngine;

/// BN254 curve implementation for Ethereum/EVM compatibility.
///
/// This is the primary curve backend for samaharam, chosen for
/// its native EVM support via precompiled contracts.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Bn254;

impl PairingEngine for Bn254 {
    type Fr = Fr;
    type G1Affine = G1Affine;
    type G1 = G1;
    type G2Affine = G2Affine;
    type Gt = Gt;

    fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt {
        Bn256::pairing(p, q)
    }

    fn multi_pairing<'a>(
        pairs: impl IntoIterator<Item = (&'a Self::G1Affine, &'a Self::G2Affine)>,
    ) -> Self::Gt {
        let pairs: Vec<_> = pairs.into_iter().collect();
        if pairs.is_empty() {
            return Gt::identity();
        }

        // Accumulate pairings - for simplicity use individual pairings
        // A more optimized version would use multi_miller_loop
        let mut result = Gt::identity();
        for (p, q) in pairs {
            result += Bn256::pairing(p, q);
        }
        result
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ff::Field;
    use group::{Curve, Group};
    use rand::rngs::OsRng;

    #[test]
    fn bn254_pairing_is_bilinear() {
        // e(aP, bQ) = e(P, Q)^(ab)
        let a = Fr::random(OsRng);
        let b = Fr::random(OsRng);

        let p = G1::random(OsRng);
        let q = halo2curves::bn256::G2::random(OsRng);

        let ap = (p * a).to_affine();
        let bq = (q * b).to_affine();
        let p_affine = p.to_affine();
        let q_affine = q.to_affine();

        let lhs = Bn254::pairing(&ap, &bq);
        let rhs = Bn254::pairing(&p_affine, &q_affine) * (a * b);

        assert_eq!(lhs, rhs);
    }

    #[test]
    fn bn254_pairing_identity() {
        let p = G1::identity().to_affine();
        let q = halo2curves::bn256::G2::random(OsRng).to_affine();

        let result = Bn254::pairing(&p, &q);
        assert_eq!(result, Gt::identity());
    }

    #[test]
    fn bn254_multi_pairing_empty() {
        let pairs: Vec<(&G1Affine, &G2Affine)> = vec![];
        let result = Bn254::multi_pairing(pairs);
        assert_eq!(result, Gt::identity());
    }

    #[test]
    fn bn254_multi_pairing_single() {
        let p = G1::random(OsRng).to_affine();
        let q = halo2curves::bn256::G2::random(OsRng).to_affine();

        let single = Bn254::pairing(&p, &q);
        let multi = Bn254::multi_pairing([(&p, &q)]);

        assert_eq!(single, multi);
    }
}
