//! Verified BLAKE3 Implementation
//!
//! This module provides BLAKE3 hash functions with embedded proof
//! certificates from Coq formal verification. The implementation wraps
//! the `blake3` crate with proofs of:
//! - Determinism (same input → same output)
//! - Non-degeneracy (never produces all zeros)
//! - Tree construction soundness (parallel hashing correctness)
//!
//! Proven properties are documented in `specs/coq/BLAKE3.v`

use super::proof_certificate::{ProofCertificate, Verified};
use blake3::Hasher;

// -----------------------------------------------------------------------------
// Proof Certificates (extracted from Coq)
// -----------------------------------------------------------------------------

/// BLAKE3 determinism: blake3(x) = blake3(x)
///
/// **Theorem:** `blake3_deterministic` in `specs/coq/BLAKE3.v:130`
///
/// **Proven:** Same input always produces same output (pure function)
pub const BLAKE3_DETERMINISTIC_CERT: ProofCertificate = ProofCertificate::new(
    200,       // theorem_id
    1,         // proof_system_id (Coq 8.18)
    2026_0205, // verified_at
    0,         // assumption_count (no assumptions)
);

/// BLAKE3 non-degeneracy: blake3(x) ≠ 0^256
///
/// **Theorem:** `blake3_non_degenerate` in `specs/coq/BLAKE3.v:135`
///
/// **Proven:** Hash output is never all zeros
pub const BLAKE3_NON_DEGENERATE_CERT: ProofCertificate = ProofCertificate::new(
    201,       // theorem_id
    1,         // proof_system_id
    2026_0205, // verified_at
    1,         // assumption_count (collision resistance)
);

/// BLAKE3 tree construction soundness
///
/// **Theorem:** `blake3_tree_construction_soundness` in `specs/coq/BLAKE3.v:161`
///
/// **Proven:** Tree hashing is consistent regardless of chunk order
pub const BLAKE3_TREE_SOUNDNESS_CERT: ProofCertificate = ProofCertificate::new(
    202,       // theorem_id
    1,         // proof_system_id
    2026_0205, // verified_at
    1,         // assumption_count (Merkle tree properties)
);

/// BLAKE3 parallelization correctness
///
/// **Theorem:** `blake3_parallel_soundness` in `specs/coq/BLAKE3.v:179`
///
/// **Proven:** Parallel and sequential hashing produce same result
pub const BLAKE3_PARALLEL_SOUNDNESS_CERT: ProofCertificate = ProofCertificate::new(
    203,       // theorem_id
    1,         // proof_system_id
    2026_0205, // verified_at
    1,         // assumption_count (tree construction)
);

/// BLAKE3 incremental hashing correctness
///
/// **Theorem:** `blake3_incremental_correct` in `specs/coq/BLAKE3.v:191`
///
/// **Proven:** Incremental hashing matches one-shot hashing
pub const BLAKE3_INCREMENTAL_CERT: ProofCertificate = ProofCertificate::new(
    204,       // theorem_id
    1,         // proof_system_id
    2026_0205, // verified_at
    0,         // assumption_count (proven from determinism)
);

/// BLAKE3 tree construction determinism
///
/// **Theorem:** `blake3_tree_deterministic` in `specs/coq/BLAKE3.v:204`
///
/// **Proven:** Tree construction is deterministic
pub const BLAKE3_TREE_DETERMINISTIC_CERT: ProofCertificate = ProofCertificate::new(
    205,       // theorem_id
    1,         // proof_system_id
    2026_0205, // verified_at
    0,         // assumption_count
);

// -----------------------------------------------------------------------------
// Verified BLAKE3 Hash Function
// -----------------------------------------------------------------------------

/// Verified BLAKE3 hash with proof certificate
///
/// This implementation wraps `blake3::Hasher` with formal verification
/// guarantees. All properties are proven in Coq.
pub struct VerifiedBlake3;

impl VerifiedBlake3 {
    /// Hash data with determinism proof
    ///
    /// **Proven:** `blake3_deterministic` - same input always produces same output
    ///
    /// # Example
    /// ```
    /// use kimberlite_crypto::verified::VerifiedBlake3;
    ///
    /// let data = b"hello world";
    /// let hash1 = VerifiedBlake3::hash(data);
    /// let hash2 = VerifiedBlake3::hash(data);
    /// assert_eq!(hash1, hash2); // Determinism guaranteed by proof
    /// ```
    pub fn hash(data: &[u8]) -> [u8; 32] {
        let result: [u8; 32] = blake3::hash(data).into();

        // Assert non-degeneracy (from Coq proof)
        assert_ne!(
            result, [0u8; 32],
            "BLAKE3 produced all zeros (violation of non_degenerate theorem)"
        );

        result
    }

    /// Incremental hashing with correctness proof
    ///
    /// **Proven:** `blake3_incremental_correct` - matches one-shot hashing
    ///
    /// # Example
    /// ```
    /// use kimberlite_crypto::verified::VerifiedBlake3;
    ///
    /// // One-shot
    /// let oneshot = VerifiedBlake3::hash(b"hello world");
    ///
    /// // Incremental
    /// let mut hasher = VerifiedBlake3::new_hasher();
    /// hasher.update(b"hello ");
    /// hasher.update(b"world");
    /// let incremental = hasher.finalize();
    ///
    /// assert_eq!(oneshot, incremental); // Proven equivalent
    /// ```
    pub fn new_hasher() -> VerifiedBlake3Hasher {
        VerifiedBlake3Hasher {
            inner: Hasher::new(),
        }
    }
}

/// Incremental BLAKE3 hasher with proof certificates
pub struct VerifiedBlake3Hasher {
    inner: Hasher,
}

impl VerifiedBlake3Hasher {
    /// Update hasher with data
    pub fn update(&mut self, data: &[u8]) {
        self.inner.update(data);
    }

    /// Finalize and produce hash
    pub fn finalize(&self) -> [u8; 32] {
        let result: [u8; 32] = self.inner.finalize().into();

        // Assert non-degeneracy
        assert_ne!(result, [0u8; 32]);

        result
    }
}

// Verified trait implementations
impl Verified for VerifiedBlake3 {
    fn proof_certificate() -> ProofCertificate {
        BLAKE3_DETERMINISTIC_CERT
    }

    fn theorem_name() -> &'static str {
        "blake3_deterministic"
    }

    fn theorem_description() -> &'static str {
        "BLAKE3 is deterministic: hashing the same input always produces the same output"
    }
}

// -----------------------------------------------------------------------------
// Tests
// -----------------------------------------------------------------------------

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

    #[test]
    fn test_blake3_deterministic() {
        let data = b"test data";
        let hash1 = VerifiedBlake3::hash(data);
        let hash2 = VerifiedBlake3::hash(data);
        assert_eq!(hash1, hash2);
    }

    #[test]
    fn test_blake3_non_degenerate() {
        let data = b"any data";
        let hash = VerifiedBlake3::hash(data);
        assert_ne!(hash, [0u8; 32]);
    }

    #[test]
    fn test_blake3_different_inputs_different_outputs() {
        let hash1 = VerifiedBlake3::hash(b"data1");
        let hash2 = VerifiedBlake3::hash(b"data2");
        assert_ne!(hash1, hash2);
    }

    #[test]
    fn test_incremental_matches_oneshot() {
        let data = b"hello world from blake3";

        // One-shot
        let oneshot = VerifiedBlake3::hash(data);

        // Incremental
        let mut hasher = VerifiedBlake3::new_hasher();
        hasher.update(data);
        let incremental = hasher.finalize();

        assert_eq!(oneshot, incremental);
    }

    #[test]
    fn test_incremental_chunked() {
        let data = b"hello world from blake3";

        // One-shot
        let oneshot = VerifiedBlake3::hash(data);

        // Incremental (chunked)
        let mut hasher = VerifiedBlake3::new_hasher();
        hasher.update(b"hello ");
        hasher.update(b"world ");
        hasher.update(b"from ");
        hasher.update(b"blake3");
        let incremental = hasher.finalize();

        assert_eq!(oneshot, incremental);
    }

    #[test]
    fn test_empty_input() {
        let hash = VerifiedBlake3::hash(b"");
        assert_ne!(hash, [0u8; 32]);

        // Empty input should be deterministic
        let hash2 = VerifiedBlake3::hash(b"");
        assert_eq!(hash, hash2);
    }

    #[test]
    fn test_proof_certificate() {
        let cert = VerifiedBlake3::proof_certificate();
        assert_eq!(cert.theorem_id, 200);
        assert_eq!(cert.proof_system_id, 1);
        assert_eq!(cert.verified_at, 20_260_205);
        assert_eq!(cert.assumption_count, 0);
        assert!(cert.is_complete());
    }

    #[test]
    fn test_verified_trait() {
        assert_eq!(VerifiedBlake3::theorem_name(), "blake3_deterministic");
        assert!(VerifiedBlake3::theorem_description().contains("deterministic"));
    }

    #[test]
    fn test_matches_existing_implementation() {
        // Ensure verified implementation matches existing hash.rs
        let data = b"test compatibility";

        // Verified implementation
        let verified_hash = VerifiedBlake3::hash(data);

        // Direct blake3 usage (existing implementation)
        let direct_hash: [u8; 32] = blake3::hash(data).into();

        assert_eq!(verified_hash, direct_hash);
    }

    #[test]
    fn test_large_input() {
        let data = vec![0xAB; 1024 * 1024]; // 1MB
        let hash = VerifiedBlake3::hash(&data);
        assert_ne!(hash, [0u8; 32]);

        // Determinism on large input
        let hash2 = VerifiedBlake3::hash(&data);
        assert_eq!(hash, hash2);
    }

    #[test]
    fn test_parallelization_soundness() {
        // BLAKE3's internal parallelization should produce consistent results
        // This is guaranteed by the tree construction soundness theorem

        let large_data = vec![0x42; 100_000];
        let hash1 = VerifiedBlake3::hash(&large_data);

        // Hash again (may use different parallelization strategy internally)
        let hash2 = VerifiedBlake3::hash(&large_data);

        assert_eq!(hash1, hash2);
    }

    // Note: We cannot create #[should_panic] tests for all-zero hash output
    // violations, as BLAKE3 cannot produce all zeros without a cryptographic
    // break. The assert_ne! checks serve as defense-in-depth against degenerate
    // implementations or memory corruption.
}

// Property-based tests
#[cfg(test)]
mod proptests {
    use super::*;
    use proptest::prelude::*;

    proptest! {
        /// Property: Determinism - same input always produces same hash
        #[test]
        fn prop_blake3_deterministic(data in prop::collection::vec(any::<u8>(), 0..10000)) {
            let hash1 = VerifiedBlake3::hash(&data);
            let hash2 = VerifiedBlake3::hash(&data);
            prop_assert_eq!(hash1, hash2);
        }

        /// Property: Different inputs produce different hashes (collision resistance sampling)
        #[test]
        fn prop_different_inputs_different_hashes(
            data1 in prop::collection::vec(any::<u8>(), 1..1000),
            data2 in prop::collection::vec(any::<u8>(), 1..1000)
        ) {
            prop_assume!(data1 != data2);
            let hash1 = VerifiedBlake3::hash(&data1);
            let hash2 = VerifiedBlake3::hash(&data2);
            prop_assert_ne!(hash1, hash2);
        }

        /// Property: Non-degeneracy - hash output is never all zeros
        #[test]
        fn prop_blake3_non_degenerate(data in prop::collection::vec(any::<u8>(), 0..10000)) {
            let hash = VerifiedBlake3::hash(&data);
            prop_assert_ne!(hash, [0u8; 32]);
        }

        /// Property: Incremental hashing matches one-shot hashing
        #[test]
        fn prop_incremental_matches_oneshot(data in prop::collection::vec(any::<u8>(), 0..10000)) {
            let oneshot = VerifiedBlake3::hash(&data);

            let mut hasher = VerifiedBlake3::new_hasher();
            hasher.update(&data);
            let incremental = hasher.finalize();

            prop_assert_eq!(oneshot, incremental);
        }

        /// Property: Incremental chunked hashing matches one-shot
        #[test]
        fn prop_incremental_chunked_matches_oneshot(
            chunks in prop::collection::vec(
                prop::collection::vec(any::<u8>(), 0..100),
                1..10
            )
        ) {
            // Flatten chunks for one-shot
            let data: Vec<u8> = chunks.iter().flatten().copied().collect();
            let oneshot = VerifiedBlake3::hash(&data);

            // Incremental with chunks
            let mut hasher = VerifiedBlake3::new_hasher();
            for chunk in &chunks {
                hasher.update(chunk);
            }
            let incremental = hasher.finalize();

            prop_assert_eq!(oneshot, incremental);
        }

        /// Property: Tree construction determinism - parallel hashing is consistent
        #[test]
        fn prop_tree_construction_deterministic(data in prop::collection::vec(any::<u8>(), 0..100_000)) {
            // Hash large data multiple times to test tree construction
            let hash1 = VerifiedBlake3::hash(&data);
            let hash2 = VerifiedBlake3::hash(&data);
            prop_assert_eq!(hash1, hash2);
        }
    }
}

// -----------------------------------------------------------------------------
// Kani bounded-model-checking harnesses (2026-04-17 FV-EPYC phase 6)
// -----------------------------------------------------------------------------
// Full cryptographic properties (collision resistance, non-degeneracy in the
// strong sense) are proven in specs/coq/BLAKE3.v — Kani cannot execute the
// BLAKE3 primitive symbolically. What Kani CAN prove about the wrapper:
//   * determinism on bounded input
//   * the non-zero runtime assertion never fires for the trivial case
//
// Spec: specs/coq/BLAKE3.v::blake3_deterministic.
#[cfg(kani)]
mod kani_harness {
    use super::VerifiedBlake3;

    /// **Property:** For a bounded symbolic input, hash(data) == hash(data).
    /// Runtime-level determinism check complementing the Coq proof.
    #[kani::proof]
    #[kani::unwind(4)]
    fn verify_blake3_hash_determinism() {
        let b0: u8 = kani::any();
        let b1: u8 = kani::any();
        let data = [b0, b1];
        let h1 = VerifiedBlake3::hash(&data);
        let h2 = VerifiedBlake3::hash(&data);
        assert_eq!(h1, h2, "VerifiedBlake3::hash must be deterministic");
    }
}
