//! Comparison and equality operators for `DataValue`.
//!
//! This module implements the standard comparison traits (`PartialEq`, `Eq`, `PartialOrd`, `Ord`)
//! and `Hash` for `DataValue`, enabling values to be compared, sorted, and used in hash-based
//! collections like `HashMap` and `HashSet`.
//!
//! # Equality
//!
//! Two `DataValue` instances are equal if they have the same variant and the same value.
//! Floating point values use epsilon comparison to handle precision issues.
//!
//! # Ordering
//!
//! When comparing values of the same type, natural ordering is used.
//! When comparing values of different types, a type hierarchy determines ordering:
//! `Null < Bool < Numbers < EnumNumber < String < Bytes < Vec < Map`
//!
//! # Hashing
//!
//! All `DataValue` variants can be hashed, including floating point numbers
//! (which are hashed by their bit representation).

use super::DataValue;
use std::hash::{Hash, Hasher};

/// Hash implementation for DataValue.
///
/// Note: Floating point values are hashed using their bit representation.
/// This means that NaN values will hash, but different NaN representations
/// may produce different hashes. Additionally, -0.0 and +0.0 will have
/// different hashes.
impl Hash for DataValue {
    fn hash<H: Hasher>(&self, state: &mut H) {
        use DataValue::*;
        // Hash a discriminant first to ensure different variants have different hashes
        std::mem::discriminant(self).hash(state);

        match self {
            Bool(v) => v.hash(state),
            U32(v) => v.hash(state),
            I32(v) => v.hash(state),
            U8(v) => v.hash(state),
            U64(v) => v.hash(state),
            I64(v) => v.hash(state),
            U128(v) => v.hash(state),
            I128(v) => v.hash(state),
            // For floats, hash their bit representation
            F32(v) => v.to_bits().hash(state),
            F64(v) => v.to_bits().hash(state),
            String(v) => v.hash(state),
            Bytes(v) => v.hash(state),
            Vec(v) => v.hash(state),
            Map(v) => {
                // HashMap doesn't have a stable iteration order, so we need to hash it carefully
                // We'll collect keys, sort them, and hash key-value pairs in order
                let mut items: std::vec::Vec<_> = v.iter().collect();
                items.sort_by(|a, b| a.0.cmp(b.0));
                items.hash(state);
            }
            EnumNumber(v) => v.hash(state),
            Null => {}
        }
    }
}

impl PartialOrd for DataValue {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}
impl Ord for DataValue {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        use DataValue::*;

        // Compare values of the same type
        match (self, other) {
            (Bool(l0), Bool(r0)) => l0.cmp(r0),
            (U32(l0), U32(r0)) => l0.cmp(r0),
            (I32(l0), I32(r0)) => l0.cmp(r0),
            (U8(l0), U8(r0)) => l0.cmp(r0),
            (U64(l0), U64(r0)) => l0.cmp(r0),
            (I64(l0), I64(r0)) => l0.cmp(r0),
            (U128(l0), U128(r0)) => l0.cmp(r0),
            (I128(l0), I128(r0)) => l0.cmp(r0),
            (F32(l0), F32(r0)) => {
                // Handle NaN cases explicitly for deterministic ordering
                l0.partial_cmp(r0).unwrap_or_else(|| {
                    // NaN is considered less than any number
                    match (l0.is_nan(), r0.is_nan()) {
                        (true, true) => std::cmp::Ordering::Equal,
                        (true, false) => std::cmp::Ordering::Less,
                        (false, true) => std::cmp::Ordering::Greater,
                        (false, false) => unreachable!(),
                    }
                })
            }
            (F64(l0), F64(r0)) => {
                // Handle NaN cases explicitly for deterministic ordering
                l0.partial_cmp(r0).unwrap_or_else(|| {
                    // NaN is considered less than any number
                    match (l0.is_nan(), r0.is_nan()) {
                        (true, true) => std::cmp::Ordering::Equal,
                        (true, false) => std::cmp::Ordering::Less,
                        (false, true) => std::cmp::Ordering::Greater,
                        (false, false) => unreachable!(),
                    }
                })
            }
            (String(l0), String(r0)) => l0.cmp(r0),
            (Bytes(l0), Bytes(r0)) => l0.cmp(r0),
            (Vec(l0), Vec(r0)) => l0.cmp(r0),
            (Map(l0), Map(r0)) => {
                // Compare maps by sorted key-value pairs for deterministic ordering
                let mut l_items: std::vec::Vec<_> = l0.iter().collect();
                let mut r_items: std::vec::Vec<_> = r0.iter().collect();
                l_items.sort_by(|a, b| a.0.cmp(b.0));
                r_items.sort_by(|a, b| a.0.cmp(b.0));
                l_items.cmp(&r_items)
            }
            (EnumNumber(l0), EnumNumber(r0)) => l0.cmp(r0),
            (Null, Null) => std::cmp::Ordering::Equal,
            // For cross-type comparisons, consistently return Less to maintain
            // a total ordering without imposing semantic meaning
            _ => std::cmp::Ordering::Less,
        }
    }
}
/// Partial equality implementation for `DataValue`.
///
/// Values are equal if they are the same variant and contain equal values.
/// Floating point equality uses epsilon comparison to handle precision issues.
impl std::cmp::PartialEq for DataValue {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
            (Self::U32(l0), Self::U32(r0)) => l0 == r0,
            (Self::I32(l0), Self::I32(r0)) => l0 == r0,
            (Self::U8(l0), Self::U8(r0)) => l0 == r0,
            (Self::U64(l0), Self::U64(r0)) => l0 == r0,
            (Self::I64(l0), Self::I64(r0)) => l0 == r0,
            (Self::U128(l0), Self::U128(r0)) => l0 == r0,
            (Self::I128(l0), Self::I128(r0)) => l0 == r0,
            (Self::F32(l0), Self::F32(r0)) => (l0 - r0).abs() < f32::EPSILON,
            (Self::F64(l0), Self::F64(r0)) => (l0 - r0).abs() < f64::EPSILON,
            (Self::String(l0), Self::String(r0)) => l0 == r0,
            (Self::Bytes(l0), Self::Bytes(r0)) => l0 == r0,
            (Self::Vec(l0), Self::Vec(r0)) => l0.eq(r0),
            (Self::Map(l0), Self::Map(r0)) => l0.eq(r0),
            (Self::EnumNumber(l0), Self::EnumNumber(r0)) => l0.eq(r0),
            (Self::Null, Self::Null) => true,
            _ => false,
        }
    }
}

impl std::cmp::Eq for DataValue {}

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

    #[rstest]
    #[case(DataValue::U8(1), DataValue::U8(1), true)]
    #[case(DataValue::U8(1), DataValue::U8(2), false)]
    #[case(DataValue::U8(1), DataValue::U32(1), false)]
    #[case(DataValue::U8(1), DataValue::I32(1), false)]
    #[case(DataValue::U8(1), DataValue::U64(1), false)]
    #[case(DataValue::U8(1), DataValue::I64(1), false)]
    #[case(DataValue::U8(1), DataValue::U128(1), false)]
    #[case(DataValue::U8(1), DataValue::I128(1), false)]
    #[case(DataValue::U8(1), DataValue::F32(1.0), false)]
    #[case(DataValue::U8(1), DataValue::F64(1.0), false)]
    #[case(DataValue::U8(1), DataValue::String("1".into()), false)]
    #[case(DataValue::U8(1), DataValue::Bytes(b"1".to_vec()), false)]
    #[case(DataValue::U8(1), DataValue::Vec(vec![1.into()]), false)]
    #[case(DataValue::U8(1), DataValue::Map(crate::stdhashmap!("a" => 1)), false)]
    #[case(DataValue::U8(1), DataValue::Null, false)]
    fn test_eq(#[case] left: DataValue, #[case] right: DataValue, #[case] expected: bool) {
        assert_eq!(left == right, expected);
        assert_eq!(left.cmp(&right), expected.cmp(&true));
    }

    #[rstest]
    #[case(DataValue::Vec(
        vec![
            DataValue::U8(1), DataValue::U32(2), DataValue::U64(3), DataValue::U128(4), DataValue::I128(5), DataValue::I32(1), DataValue::I64(1),
             DataValue::I128(5), DataValue::F32(6.0), DataValue::F64(7.0), DataValue::String("8".into()),
              DataValue::Bytes(b"9".to_vec()), DataValue::Vec(vec![DataValue::U8(10)]), 
              DataValue::Map(crate::stdhashmap!("a" => 11))
        ]
    ), DataValue::Vec(vec![
        DataValue::U8(1), DataValue::U32(2), DataValue::U64(3), DataValue::U128(4), DataValue::I128(5), DataValue::I32(1), DataValue::I64(1),
        DataValue::I128(5), DataValue::F32(6.0), DataValue::F64(7.0), DataValue::String("8".into()),
         DataValue::Bytes(b"9".to_vec()), DataValue::Vec(vec![DataValue::U8(10)]), 
         DataValue::Map(crate::stdhashmap!("a" => 11))]), std::cmp::Ordering::Equal)
    ]
    #[case(DataValue::Vec(
        vec![
            DataValue::U8(1), DataValue::U32(2), DataValue::U64(3), DataValue::U128(4), DataValue::I128(5), DataValue::I32(1), DataValue::I64(1),
             DataValue::I128(5), DataValue::F32(6.0), DataValue::F64(7.0), DataValue::String("8".into()),
              DataValue::Bytes(b"9".to_vec()), DataValue::Vec(vec![DataValue::U8(10)]), 
              DataValue::Map(crate::stdhashmap!("a" => 11))
        ]
    ), DataValue::Vec(
        vec![DataValue::U8(1), DataValue::U8(2), DataValue::U8(3)]
    ), std::cmp::Ordering::Less)
    ]
    fn test_cmp(
        #[case] left: DataValue,
        #[case] right: DataValue,
        #[case] expected: std::cmp::Ordering,
    ) {
        assert_eq!(left.cmp(&right), expected);
    }
}
