use ark_bn254::{Bn254, Fr};
use ark_groth16::{Groth16, Proof, VerifyingKey, prepare_verifying_key};
use ark_serialize::CanonicalDeserialize;
use ark_snark::SNARK;
use base64::Engine;
use serde_json::Value;
use crate::binding;
use crate::error::VerifyError;
use crate::VerifyConfig;
pub fn verify_if_present(p: &Value, cfg: &VerifyConfig) -> Result<(), VerifyError> {
let proof_evidence = p["verification"]["evidence"].as_array()
.and_then(|arr| arr.iter().find(|e| e["type"] == "cryptographic_proof"))
.ok_or_else(|| VerifyError::SchemaViolation("no cryptographic_proof evidence".into()))?;
let proof_b64 = match proof_evidence.get("proof").and_then(|v| v.as_str()) {
Some(s) if !s.is_empty() => s,
_ => return Ok(()), };
let proof_bytes = base64::engine::general_purpose::STANDARD.decode(proof_b64)
.map_err(|e| VerifyError::ProofInvalid(format!("proof not base64: {e}")))?;
let proof = Proof::<Bn254>::deserialize_compressed(&proof_bytes[..])
.or_else(|_| Proof::<Bn254>::deserialize_uncompressed(&proof_bytes[..]))
.map_err(|e| VerifyError::ProofInvalid(format!("proof deserialize failed: {e}")))?;
let vk_hash = p["verification"]["public_inputs"]["vk_hash"].as_str().unwrap_or("");
let vk_bytes = cfg.lookup_vk(vk_hash)
.ok_or_else(|| VerifyError::UnknownVk(vk_hash.to_string()))?;
let vk = VerifyingKey::<Bn254>::deserialize_compressed(vk_bytes)
.or_else(|_| VerifyingKey::<Bn254>::deserialize_uncompressed(vk_bytes))
.map_err(|e| VerifyError::ProofInvalid(format!("vk deserialize failed: {e}")))?;
let pi = &p["verification"]["public_inputs"];
let action_hash = pi["action_hash"].as_str().unwrap_or("");
let contract_hash = pi["contract_hash"].as_str().unwrap_or("");
let decision_bit = pi["decision_bit"].as_i64().unwrap_or(-1) as u8;
let inputs_fr: Vec<Fr> = binding::public_inputs_fr(
action_hash, contract_hash, decision_bit, vk_hash,
)?;
let pvk = prepare_verifying_key(&vk);
let ok = Groth16::<Bn254>::verify_with_processed_vk(&pvk, &inputs_fr, &proof)
.map_err(|e| VerifyError::ProofInvalid(format!("verify call failed: {e}")))?;
if !ok {
return Err(VerifyError::ProofInvalid("verify returned false".into()));
}
Ok(())
}