#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]
#![allow(non_upper_case_globals)]

extern crate link_cplusplus;

mod bindings {
    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}

pub fn validate_proof(
    plot_id: &[u8; 32],
    k: u8,
    challenge: &[u8; 32],
    proof: &[u8],
    quality: &mut [u8; 32],
) -> bool {
    let Some(proof_len) = proof.len().try_into().ok() else {
        return false;
    };

    unsafe {
        bindings::validate_proof(
            plot_id.as_ptr(),
            k,
            challenge.as_ptr(),
            proof.as_ptr(),
            proof_len,
            quality.as_mut_ptr(),
        )
    }
}

#[cfg(test)]
mod tests {
    use std::{fs, path::PathBuf};

    use super::*;

    #[test]
    fn test_proofs() {
        /*
         * Generated by tools/generate_rust_proof_test.py
         */
        let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test_proofs.txt");
        let proofs = fs::read_to_string(path).unwrap();

        for line in proofs.lines() {
            let mut parts = line.split(", ");
            let plot_id = hex::decode(parts.next().unwrap())
                .unwrap()
                .try_into()
                .unwrap();
            let k = parts.next().unwrap().parse().unwrap();
            let challenge = hex::decode(parts.next().unwrap())
                .unwrap()
                .try_into()
                .unwrap();
            let proof = hex::decode(parts.next().unwrap()).unwrap();
            let quality = hex::decode(parts.next().unwrap()).unwrap();

            // The test should pass with the initial data.
            let mut actual_quality = [0; 32];
            assert!(validate_proof(
                &plot_id,
                k,
                &challenge,
                &proof,
                &mut actual_quality
            ));
            assert_eq!(actual_quality.as_slice(), quality);

            // If you change the seed, the test should fail.
            let mut actual_quality = [0; 32];
            let mut new_plot_id = plot_id;
            new_plot_id[0] = new_plot_id[0].wrapping_add(1);
            assert!(!validate_proof(
                &new_plot_id,
                k,
                &challenge,
                &proof,
                &mut actual_quality
            ));
            assert_eq!(actual_quality, [0; 32]);

            // If you change the K size, the test should fail.
            let mut actual_quality = [0; 32];
            let new_k = k.wrapping_add(1);
            assert!(!validate_proof(
                &plot_id,
                new_k,
                &challenge,
                &proof,
                &mut actual_quality
            ));

            // If you change the challenge, the test should fail.
            let mut actual_quality = [0; 32];
            let mut new_challenge = challenge;
            new_challenge[0] = new_challenge[0].wrapping_add(1);
            assert!(!validate_proof(
                &plot_id,
                k,
                &new_challenge,
                &proof,
                &mut actual_quality
            ));
            assert_eq!(actual_quality, [0; 32]);

            // If you change the proof, the test should fail.
            let mut actual_quality = [0; 32];
            let mut new_proof = proof;
            new_proof[0] = new_proof[0].wrapping_add(1);
            assert!(!validate_proof(
                &plot_id,
                k,
                &challenge,
                &new_proof,
                &mut actual_quality
            ));
            assert_eq!(actual_quality, [0; 32]);
        }
    }

    #[test]
    fn test_empty_proof() {
        let mut quality = [0; 32];
        assert!(!validate_proof(&[0; 32], 32, &[0; 32], &[], &mut quality));
        assert_eq!(quality, [0; 32]);
    }

    #[test]
    fn test_min_k_size() {
        let mut quality = [0; 32];
        assert!(!validate_proof(&[0; 32], 0, &[0; 32], &[0], &mut quality));
        assert_eq!(quality, [0; 32]);
    }

    #[test]
    fn test_max_k_size() {
        let mut quality = [0; 32];
        assert!(!validate_proof(&[0; 32], 100, &[0; 32], &[0], &mut quality));
        assert_eq!(quality, [0; 32]);
    }

    #[test]
    fn test_wrong_proof_length() {
        let mut quality = [0; 32];
        assert!(!validate_proof(
            &[0; 32],
            32,
            &[0; 32],
            &[0; 1000],
            &mut quality
        ));
        assert_eq!(quality, [0; 32]);
    }

    #[test]
    fn test_bad_proof() {
        let mut quality = [0; 32];
        assert!(!validate_proof(
            &[0; 32],
            32,
            &[0; 32],
            &[0; 32 * 8],
            &mut quality
        ));
        assert_eq!(quality, [0; 32]);
    }
}
