//! Property-based tests for CRDT vote store.

use super::vote_store::VoteCountState;
use proptest::prelude::*;

// Property: VoteCountState merge is commutative
proptest! {
    #[test]
    fn merge_commutative(
        count_a in 0u64..1000,
        count_b in 0u64..1000,
        weight_a in 0.0f32..100.0,
        weight_b in 0.0f32..100.0,
    ) {
        let hash = [1u8; 32];
        let node1 = [1u8; 16];
        let node2 = [2u8; 16];

        let state_a = VoteCountState::new(hash, count_a, weight_a, node1);
        let state_b = VoteCountState::new(hash, count_b, weight_b, node2);

        let merged_ab = state_a.merge(&state_b);
        let merged_ba = state_b.merge(&state_a);

        // Count and weight should be the same regardless of merge order
        prop_assert_eq!(merged_ab.count, merged_ba.count);
        prop_assert!((merged_ab.weight - merged_ba.weight).abs() < 0.0001);
    }
}

// Property: VoteCountState merge is associative
proptest! {
    #[test]
    fn merge_associative(
        count_a in 0u64..1000,
        count_b in 0u64..1000,
        count_c in 0u64..1000,
        weight_a in 0.0f32..100.0,
        weight_b in 0.0f32..100.0,
        weight_c in 0.0f32..100.0,
    ) {
        let hash = [1u8; 32];
        let node1 = [1u8; 16];
        let node2 = [2u8; 16];
        let node3 = [3u8; 16];

        let state_a = VoteCountState::new(hash, count_a, weight_a, node1);
        let state_b = VoteCountState::new(hash, count_b, weight_b, node2);
        let state_c = VoteCountState::new(hash, count_c, weight_c, node3);

        // (A merge B) merge C
        let merged_ab_c = state_a.merge(&state_b).merge(&state_c);
        // A merge (B merge C)
        let merged_a_bc = state_a.merge(&state_b.merge(&state_c));

        prop_assert_eq!(merged_ab_c.count, merged_a_bc.count);
        prop_assert!((merged_ab_c.weight - merged_a_bc.weight).abs() < 0.0001);
    }
}

// Property: VoteCountState merge is idempotent
proptest! {
    #[test]
    fn merge_idempotent(
        count in 0u64..1000,
        weight in 0.0f32..100.0,
    ) {
        let hash = [1u8; 32];
        let node_id = [1u8; 16];

        let state = VoteCountState::new(hash, count, weight, node_id);
        let merged = state.merge(&state);

        prop_assert_eq!(state.count, merged.count);
        prop_assert!((state.weight - merged.weight).abs() < 0.0001);
    }
}
