//! Verifiable secret sharing and share verification.

use crate::polynomial::mod_pow;

/// A generator for Feldman-style verifiable secret sharing.
/// Commitments to polynomial coefficients allow verification of shares.
pub struct VerifiableSharing {
    /// Generator g for commitments.
    generator: u64,
}

impl VerifiableSharing {
    /// Create a new verifiable sharing instance with a given generator.
    pub fn new(generator: u64) -> Self {
        Self { generator }
    }

    /// Create commitments to the polynomial coefficients.
    /// commitments[i] = g^coeffs[i] mod PRIME
    pub fn commit(&self, coeffs: &[u64]) -> Vec<u64> {
        coeffs.iter().map(|&c| mod_pow(self.generator, c)).collect()
    }

    /// Verify a share against the commitments.
    /// Checks that g^y == product of commitments[i]^(x^i) for all i.
    pub fn verify_share(
        &self,
        share_x: u64,
        share_y: u64,
        commitments: &[u64],
    ) -> bool {
        // Compute g^y mod PRIME
        let lhs = mod_pow(self.generator, share_y);

        // Compute product of commitment[i]^(x^i)
        let mut rhs = 1u64;
        let mut x_power = 1u64;
        for &commitment in commitments {
            // commitment^(x^i) = g^(coeff_i * x^i)
            let term = mod_pow(commitment, x_power);
            rhs = crate::polynomial::mod_mul(rhs, term);
            x_power = crate::polynomial::mod_mul(x_power, share_x);
        }

        lhs == rhs
    }
}

/// Visual cryptography basics — generating and stacking binary shares.
pub struct VisualCryptography;

impl VisualCryptography {
    /// Generate 2 visual cryptography shares for a single pixel.
    /// Returns two bits: (share1_bit, share2_bit).
    /// When XORed together, they recover the original pixel.
    pub fn split_pixel(pixel: bool, rng_bit: bool) -> (bool, bool) {
        // share1 = random, share2 = share1 XOR pixel
        let s1 = rng_bit;
        let s2 = s1 ^ pixel;
        (s1, s2)
    }

    /// Recover a pixel from two visual cryptography shares (XOR).
    pub fn recover_pixel(share1: bool, share2: bool) -> bool {
        share1 ^ share2
    }

    /// Split a binary image (vec of bools) into two visual shares.
    pub fn split_image(image: &[bool]) -> (Vec<bool>, Vec<bool>) {
        let mut share1 = Vec::with_capacity(image.len());
        let mut share2 = Vec::with_capacity(image.len());
        let mut rng_state = 0xdeadbeefu64;

        for &pixel in image {
            rng_state = rng_state ^ (rng_state << 13) ^ (rng_state >> 7);
            let rng_bit = (rng_state & 1) == 1;
            let (s1, s2) = Self::split_pixel(pixel, rng_bit);
            share1.push(s1);
            share2.push(s2);
        }
        (share1, share2)
    }

    /// Recover an image from two visual shares.
    pub fn recover_image(share1: &[bool], share2: &[bool]) -> Vec<bool> {
        share1.iter().zip(share2.iter()).map(|(&a, &b)| a ^ b).collect()
    }
}

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

    #[test]
    fn test_visual_pixel_roundtrip() {
        for pixel in [true, false] {
            for rng in [true, false] {
                let (s1, s2) = VisualCryptography::split_pixel(pixel, rng);
                assert_eq!(VisualCryptography::recover_pixel(s1, s2), pixel);
            }
        }
    }

    #[test]
    fn test_visual_image_roundtrip() {
        let image = vec![true, false, true, true, false, false, true, false];
        let (s1, s2) = VisualCryptography::split_image(&image);
        let recovered = VisualCryptography::recover_image(&s1, &s2);
        assert_eq!(recovered, image);
    }

    #[test]
    fn test_visual_shares_look_random() {
        // Individual shares should not reveal the image
        let image = vec![true; 100]; // all white
        let (s1, _s2) = VisualCryptography::split_image(&image);
        // Share should have roughly 50% true bits
        let ones = s1.iter().filter(|&&b| b).count();
        assert!(ones > 20 && ones < 80, "Share doesn't look random: {ones}/100");
    }

    #[test]
    fn test_feldman_commit_verify() {
        let vs = VerifiableSharing::new(2);
        let coeffs = &[42u64, 123, 456]; // secret=42, random coeffs
        let commitments = vs.commit(coeffs);
        // Evaluate polynomial at x=1: y = 42 + 123*1 + 456*1^2 = 621
        let y = crate::polynomial::eval_poly(coeffs, 1);
        assert!(vs.verify_share(1, y, &commitments));
    }

    #[test]
    fn test_feldman_detect_corruption() {
        let vs = VerifiableSharing::new(2);
        let coeffs = &[42u64, 100];
        let commitments = vs.commit(coeffs);
        let y = crate::polynomial::eval_poly(coeffs, 1);
        // Corrupted share
        assert!(!vs.verify_share(1, y + 1, &commitments));
    }
}
