//! Testing utilities for generating real PLONK proofs.
//!
//! This module provides helpers for creating cryptographically valid proofs
//! for use in tests, replacing mock/random proof generation.

use ff::Field;
use group::{Curve, Group};
use rand::rngs::OsRng;

use crate::backend::bn254::Bn254;
use crate::crypto::{
    KzgSrs, PlonkProof, PlonkProver, ProofEvaluations, SimpleCircuit, VerificationKey,
};

use halo2curves::bn256::{Fr, G1, G2};

/// Create a test SRS with deterministic tau for reproducibility.
pub fn create_test_srs(size: usize) -> KzgSrs<Bn254> {
    // Use deterministic tau for reproducible tests
    let tau = Fr::from(0x1234567890abcdef_u64);
    let g1_gen = G1::generator();
    let g2_gen = G2::generator();

    let mut powers_of_tau_g1 = Vec::with_capacity(size);
    let mut current = Fr::ONE;
    for _ in 0..size {
        powers_of_tau_g1.push((g1_gen * current).to_affine());
        current *= tau;
    }

    KzgSrs {
        powers_of_tau_g1,
        tau_g2: (g2_gen * tau).to_affine(),
        g2_generator: g2_gen.to_affine(),
    }
}

/// Create a test verification key with specified parameters.
pub fn create_test_vk(num_public_inputs: usize, domain_size: usize) -> VerificationKey<Bn254> {
    // Use deterministic commitments for reproducibility
    let _g1_gen = G1::generator().to_affine();
    let g2_gen = G2::generator().to_affine();

    // Create selector commitments (5 for standard PLONK)
    let selector_commitments: Vec<_> = (0..5)
        .map(|i| (G1::generator() * Fr::from((i + 1) as u64)).to_affine())
        .collect();

    // Create permutation commitments (3 for 3 wires)
    let permutation_commitments: Vec<_> = (0..3)
        .map(|i| (G1::generator() * Fr::from((i + 10) as u64)).to_affine())
        .collect();

    // x_g2 = tau * G2 (for pairing checks)
    let tau = Fr::from(0x1234567890abcdef_u64);
    let x_g2 = (G2::generator() * tau).to_affine();

    VerificationKey {
        num_public_inputs,
        domain_size,
        selector_commitments,
        permutation_commitments,
        x_g2,
        g2_generator: g2_gen,
    }
}

/// Create a real PLONK proof for a simple multiplication circuit.
///
/// This generates a cryptographically valid proof for the circuit: a * b = c
pub fn create_real_plonk_proof(a: u64, b: u64) -> PlonkProof<Bn254> {
    let domain_size = 8;
    let srs = create_test_srs(domain_size * 4);
    let vk = create_test_vk(1, domain_size);
    let prover = PlonkProver::<Bn254>::new(srs, vk);

    let circuit = SimpleCircuit::<Bn254>::multiplication(Fr::from(a), Fr::from(b));

    prover.prove(&circuit).expect("Proof generation should succeed")
}

/// Create a real PLONK proof with multiple multiplication gates.
pub fn create_real_plonk_proof_multi(pairs: Vec<(u64, u64)>) -> PlonkProof<Bn254> {
    let domain_size = 16.max(pairs.len().next_power_of_two());
    let srs = create_test_srs(domain_size * 4);
    let vk = create_test_vk(pairs.len(), domain_size);
    let prover = PlonkProver::<Bn254>::new(srs, vk);

    let fr_pairs: Vec<_> = pairs.into_iter().map(|(a, b)| (Fr::from(a), Fr::from(b))).collect();
    let circuit = SimpleCircuit::<Bn254>::from_multiplications(fr_pairs);

    prover.prove(&circuit).expect("Proof generation should succeed")
}

/// Create a mock PLONK proof using random values (for backward compatibility).
///
/// Note: This creates proofs that won't pass cryptographic verification,
/// but can be used for testing serialization and data flow.
pub fn create_mock_plonk_proof() -> PlonkProof<Bn254> {
    PlonkProof {
        wire_commitments: [
            G1::random(OsRng).to_affine(),
            G1::random(OsRng).to_affine(),
            G1::random(OsRng).to_affine(),
        ],
        z_commitment: G1::random(OsRng).to_affine(),
        t_commitments: vec![
            G1::random(OsRng).to_affine(),
            G1::random(OsRng).to_affine(),
            G1::random(OsRng).to_affine(),
        ],
        opening_proof: G1::random(OsRng).to_affine(),
        shifted_opening_proof: G1::random(OsRng).to_affine(),
        evaluations: ProofEvaluations {
            a_eval: Fr::random(OsRng),
            b_eval: Fr::random(OsRng),
            c_eval: Fr::random(OsRng),
            s1_eval: Fr::random(OsRng),
            s2_eval: Fr::random(OsRng),
            z_shifted_eval: Fr::random(OsRng),
        },
    }
}

/// Create a mock verification key using random values.
pub fn create_mock_vk(num_public_inputs: usize, domain_size: usize) -> VerificationKey<Bn254> {
    VerificationKey {
        num_public_inputs,
        domain_size,
        selector_commitments: vec![
            G1::random(OsRng).to_affine(),
            G1::random(OsRng).to_affine(),
            G1::random(OsRng).to_affine(),
            G1::random(OsRng).to_affine(),
            G1::random(OsRng).to_affine(),
        ],
        permutation_commitments: vec![
            G1::random(OsRng).to_affine(),
            G1::random(OsRng).to_affine(),
            G1::random(OsRng).to_affine(),
        ],
        x_g2: G2::random(OsRng).to_affine(),
        g2_generator: G2::generator().to_affine(),
    }
}
