//! CRDT property verification helpers.
//!
//! This module provides functions to verify that CRDT invariants hold:
//!
//! - **Commutativity**: `merge(A, B) = merge(B, A)`
//! - **Associativity**: `merge(merge(A, B), C) = merge(A, merge(B, C))`
//! - **Idempotence**: `merge(A, A) = A`
//!
//! These properties are essential for eventual consistency in distributed systems.

use std::collections::HashSet;

/// Verifies commutativity of merge operations.
///
/// For G-Set CRDT (used by assertions), merge is set union which is commutative.
/// This function verifies that merging two sets in either order produces the same result.
///
/// # Arguments
///
/// * `set_a` - First set of assertion hashes
/// * `set_b` - Second set of assertion hashes
///
/// # Returns
///
/// `true` if `merge(A, B) = merge(B, A)`, `false` otherwise.
///
/// # Example
///
/// ```ignore
/// let a = vec![[1u8; 32], [2u8; 32]];
/// let b = vec![[2u8; 32], [3u8; 32]];
/// assert!(verify_commutativity(&a, &b));
/// ```
pub fn verify_commutativity(set_a: &[[u8; 32]], set_b: &[[u8; 32]]) -> bool {
    let merged_ab = merge_sets(set_a, set_b);
    let merged_ba = merge_sets(set_b, set_a);

    // Both should produce the same result
    let ab_set: HashSet<_> = merged_ab.iter().collect();
    let ba_set: HashSet<_> = merged_ba.iter().collect();

    ab_set == ba_set
}

/// Verifies associativity of merge operations.
///
/// For G-Set CRDT, `merge(merge(A, B), C) = merge(A, merge(B, C))`.
///
/// # Arguments
///
/// * `set_a` - First set of assertion hashes
/// * `set_b` - Second set of assertion hashes
/// * `set_c` - Third set of assertion hashes
///
/// # Returns
///
/// `true` if the property holds, `false` otherwise.
pub fn verify_associativity(set_a: &[[u8; 32]], set_b: &[[u8; 32]], set_c: &[[u8; 32]]) -> bool {
    // (A merge B) merge C
    let ab = merge_sets(set_a, set_b);
    let ab_c = merge_sets(&ab, set_c);

    // A merge (B merge C)
    let bc = merge_sets(set_b, set_c);
    let a_bc = merge_sets(set_a, &bc);

    // Both should produce the same result
    let ab_c_set: HashSet<_> = ab_c.iter().collect();
    let a_bc_set: HashSet<_> = a_bc.iter().collect();

    ab_c_set == a_bc_set
}

/// Verifies idempotence of merge operations.
///
/// For G-Set CRDT, `merge(A, A) = A`.
///
/// # Arguments
///
/// * `set` - Set of assertion hashes
///
/// # Returns
///
/// `true` if merging a set with itself produces the same set.
pub fn verify_idempotence(set: &[[u8; 32]]) -> bool {
    let merged = merge_sets(set, set);

    // Should be identical to original
    let original: HashSet<_> = set.iter().collect();
    let merged_set: HashSet<_> = merged.iter().collect();

    original == merged_set
}

/// Verifies all three CRDT properties at once.
///
/// # Arguments
///
/// * `set_a` - First set of assertion hashes
/// * `set_b` - Second set of assertion hashes
/// * `set_c` - Third set of assertion hashes
///
/// # Returns
///
/// A `CrdtPropertyResult` with details on which properties passed/failed.
pub fn verify_all_properties(
    set_a: &[[u8; 32]],
    set_b: &[[u8; 32]],
    set_c: &[[u8; 32]],
) -> CrdtPropertyResult {
    CrdtPropertyResult {
        commutative: verify_commutativity(set_a, set_b),
        associative: verify_associativity(set_a, set_b, set_c),
        idempotent_a: verify_idempotence(set_a),
        idempotent_b: verify_idempotence(set_b),
        idempotent_c: verify_idempotence(set_c),
    }
}

/// Result of CRDT property verification.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CrdtPropertyResult {
    /// Whether commutativity holds for A and B.
    pub commutative: bool,
    /// Whether associativity holds for A, B, and C.
    pub associative: bool,
    /// Whether idempotence holds for set A.
    pub idempotent_a: bool,
    /// Whether idempotence holds for set B.
    pub idempotent_b: bool,
    /// Whether idempotence holds for set C.
    pub idempotent_c: bool,
}

impl CrdtPropertyResult {
    /// Returns true if all properties hold.
    pub fn all_passed(&self) -> bool {
        self.commutative
            && self.associative
            && self.idempotent_a
            && self.idempotent_b
            && self.idempotent_c
    }

    /// Returns a list of failed properties.
    pub fn failures(&self) -> Vec<&'static str> {
        let mut failures = Vec::new();
        if !self.commutative {
            failures.push("commutativity");
        }
        if !self.associative {
            failures.push("associativity");
        }
        if !self.idempotent_a {
            failures.push("idempotence (set A)");
        }
        if !self.idempotent_b {
            failures.push("idempotence (set B)");
        }
        if !self.idempotent_c {
            failures.push("idempotence (set C)");
        }
        failures
    }
}

/// Merges two sets using G-Set (union) semantics.
fn merge_sets(a: &[[u8; 32]], b: &[[u8; 32]]) -> Vec<[u8; 32]> {
    let mut result: HashSet<[u8; 32]> = a.iter().copied().collect();
    result.extend(b.iter().copied());
    result.into_iter().collect()
}

/// Verifies that Merkle roots match when computed from the same assertion set
/// regardless of insertion order.
///
/// This is important for convergence verification - nodes that have the same
/// assertions should produce the same canonical Merkle root.
///
/// # Arguments
///
/// * `hashes` - Set of assertion hashes
///
/// # Returns
///
/// `true` if different orderings produce the same canonical root.
pub fn verify_merkle_order_independence(hashes: &[[u8; 32]]) -> bool {
    if hashes.is_empty() {
        return true;
    }

    // Compute canonical root (sorted order)
    let mut sorted = hashes.to_vec();
    sorted.sort();

    let mut tree1 = stemedb_merkle::MerkleTree::new();
    for h in &sorted {
        if tree1.insert(*h).is_err() {
            return false;
        }
    }
    let root1 = tree1.root().ok();

    // Compute with reverse order
    let mut reversed = hashes.to_vec();
    reversed.reverse();
    reversed.sort(); // Canonical means sorted

    let mut tree2 = stemedb_merkle::MerkleTree::new();
    for h in &reversed {
        if tree2.insert(*h).is_err() {
            return false;
        }
    }
    let root2 = tree2.root().ok();

    root1 == root2
}

/// Verifies eventual consistency: given the same set of operations,
/// different orderings should produce the same final state.
///
/// # Arguments
///
/// * `operations` - List of assertion hashes representing write operations
///
/// # Returns
///
/// `true` if all orderings converge to the same state.
pub fn verify_eventual_consistency(operations: &[[u8; 32]]) -> bool {
    if operations.len() <= 1 {
        return true;
    }

    // Try multiple orderings
    let mut orderings: Vec<Vec<[u8; 32]>> = Vec::new();

    // Original order
    orderings.push(operations.to_vec());

    // Reverse order
    let mut reversed = operations.to_vec();
    reversed.reverse();
    orderings.push(reversed);

    // Shuffled (deterministic based on content)
    let mut shuffled = operations.to_vec();
    shuffled.sort_by(|a, b| {
        // Sort by XOR of all bytes for a deterministic but different ordering
        let a_xor: u8 = a.iter().fold(0, |acc, &x| acc ^ x);
        let b_xor: u8 = b.iter().fold(0, |acc, &x| acc ^ x);
        a_xor.cmp(&b_xor)
    });
    orderings.push(shuffled);

    // Compute final state for each ordering
    let final_states: Vec<HashSet<[u8; 32]>> = orderings
        .iter()
        .map(|ordering| {
            let mut state = HashSet::new();
            for op in ordering {
                state.insert(*op);
            }
            state
        })
        .collect();

    // All final states should be identical
    final_states.windows(2).all(|w| w[0] == w[1])
}

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

    fn hash(n: u8) -> [u8; 32] {
        [n; 32]
    }

    #[test]
    fn test_commutativity() {
        let a = vec![hash(1), hash(2)];
        let b = vec![hash(2), hash(3)];

        assert!(verify_commutativity(&a, &b));
    }

    #[test]
    fn test_associativity() {
        let a = vec![hash(1)];
        let b = vec![hash(2)];
        let c = vec![hash(3)];

        assert!(verify_associativity(&a, &b, &c));
    }

    #[test]
    fn test_idempotence() {
        let set = vec![hash(1), hash(2), hash(3)];
        assert!(verify_idempotence(&set));
    }

    #[test]
    fn test_all_properties() {
        let a = vec![hash(1), hash(2)];
        let b = vec![hash(2), hash(3)];
        let c = vec![hash(3), hash(4)];

        let result = verify_all_properties(&a, &b, &c);

        assert!(result.all_passed());
        assert!(result.failures().is_empty());
    }

    #[test]
    fn test_empty_sets() {
        let empty: Vec<[u8; 32]> = vec![];
        let non_empty = vec![hash(1)];

        assert!(verify_commutativity(&empty, &non_empty));
        assert!(verify_associativity(&empty, &empty, &non_empty));
        assert!(verify_idempotence(&empty));
    }

    #[test]
    fn test_merkle_order_independence() {
        let hashes = vec![hash(1), hash(2), hash(3), hash(4), hash(5)];

        assert!(verify_merkle_order_independence(&hashes));
    }

    #[test]
    fn test_eventual_consistency() {
        let operations = vec![hash(1), hash(2), hash(3), hash(4), hash(5)];

        assert!(verify_eventual_consistency(&operations));
    }

    #[test]
    fn test_eventual_consistency_single() {
        let operations = vec![hash(1)];
        assert!(verify_eventual_consistency(&operations));
    }

    #[test]
    fn test_eventual_consistency_empty() {
        let operations: Vec<[u8; 32]> = vec![];
        assert!(verify_eventual_consistency(&operations));
    }

    #[test]
    fn test_merge_sets() {
        let a = vec![hash(1), hash(2)];
        let b = vec![hash(2), hash(3)];

        let merged = merge_sets(&a, &b);

        // Should have 3 unique elements
        let merged_set: HashSet<_> = merged.into_iter().collect();
        assert_eq!(merged_set.len(), 3);
        assert!(merged_set.contains(&hash(1)));
        assert!(merged_set.contains(&hash(2)));
        assert!(merged_set.contains(&hash(3)));
    }
}
