//! # trs-data-value
//!
//! A versatile, type-safe data value abstraction for Rust that provides seamless conversion
//! between different data formats including JSON, Protocol Buffers, Python objects, and native Rust types.
//!
//! ## Features
//!
//! - **Type-Safe Conversions**: Convert between various numeric types, strings, bytes, collections, and more
//! - **Arithmetic Operations**: Support for add, subtract, multiply, divide operations with automatic type coercion
//! - **Serialization**: Built-in serde support for JSON and Protocol Buffer serialization
//! - **Python Integration**: Optional Python bindings via pyo3 (enable with `python` feature)
//! - **Comparison & Ordering**: Full implementation of equality, ordering, and hashing traits
//! - **Extraction**: Type-safe extraction to native Rust types
//!
//! ## Example
//!
//! ```rust
//! use trs_data_value::DataValue;
//!
//! // Create values from native types
//! let num = DataValue::from(42u32);
//! let text = DataValue::from("hello");
//!
//! // Perform arithmetic operations
//! let a = DataValue::from(10i32);
//! let b = DataValue::from(5i32);
//! let sum = a + b;
//!
//! // Extract values back to native types
//! let result: i32 = sum.extract();
//! assert_eq!(result, 15);
//!
//! // Use the macro for complex structures
//! let data = trs_data_value::datavalue!(
//!     "name" => "Alice",
//!     "age" => 30,
//!     "scores" => vec![95usize, 87, 92]
//! );
//! ```

use std::collections::HashMap;

#[cfg(feature = "python")]
mod python;

mod basic_operation;
mod deserializer;
mod extract;
mod from;
mod operators;
mod utils;
pub use smartstring::alias::String;

/// A flexible, type-safe value type that can represent various data types.
///
/// `DataValue` is similar to `serde_json::Value` but provides additional benefits:
/// - Supports more numeric types (u8, i32, u32, i64, u64, i128, u128, f32, f64)
/// - Optional Python interoperability via pyo3
/// - Protocol Buffer support via prost-reflect
/// - Arithmetic operations with automatic type coercion
/// - Memory-efficient string representation using smartstring
///
/// # Variants
///
/// - `String`: UTF-8 string data (using smartstring for efficiency)
/// - `Bytes`: Raw byte array
/// - `U8`, `U32`, `U64`, `U128`: Unsigned integer types
/// - `I32`, `I64`, `I128`: Signed integer types
/// - `F32`, `F64`: Floating-point types
/// - `Bool`: Boolean value
/// - `Vec`: Ordered collection of DataValues
/// - `Map`: Key-value mapping with String keys
/// - `EnumNumber`: Protobuf enum represented as i32
/// - `Null`: Represents absence of a value
///
/// # Examples
///
/// ```rust
/// use trs_data_value::DataValue;
///
/// // Creating values
/// let number = DataValue::U32(42);
/// let text = DataValue::String("hello".into());
/// let flag = DataValue::Bool(true);
/// let nothing = DataValue::Null;
///
/// // Using From trait
/// let value: DataValue = 42u32.into();
/// let value: DataValue = "hello".into();
/// ```
#[derive(Debug, Clone, Default)]
pub enum DataValue {
    /// UTF-8 encoded string using smartstring for memory efficiency
    String(String),
    /// Raw byte array
    Bytes(Vec<u8>),
    /// 8-bit unsigned integer
    U8(u8),
    /// Boolean value
    Bool(bool),
    /// 32-bit signed integer
    I32(i32),
    /// 32-bit unsigned integer
    U32(u32),
    /// 64-bit signed integer
    I64(i64),
    /// 64-bit unsigned integer
    U64(u64),
    /// 128-bit signed integer
    I128(i128),
    /// 128-bit unsigned integer
    U128(u128),
    /// 32-bit floating point
    F32(f32),
    /// 64-bit floating point
    F64(f64),
    /// Key-value mapping
    Map(HashMap<String, DataValue>),
    /// Ordered sequence of values
    Vec(Vec<DataValue>),
    /// Protocol Buffer enum value (represented as i32)
    EnumNumber(i32),
    /// Represents absence of a value
    #[default]
    Null,
}

#[cfg(feature = "utoipa")]
mod utoipa_impl {
    use super::DataValue;
    use utoipa::openapi::schema::Schema;
    use utoipa::openapi::RefOr;

    impl utoipa::PartialSchema for DataValue {
        fn schema() -> RefOr<Schema> {
            utoipa::openapi::schema::ObjectBuilder::new().into()
        }
    }

    impl utoipa::ToSchema for DataValue {}

    #[cfg(test)]
    mod tests {
        use super::*;
        use utoipa::{PartialSchema, ToSchema};

        #[test]
        fn schema_name_is_data_value() {
            assert_eq!(DataValue::name(), "DataValue");
        }

        #[test]
        fn schema_is_object_variant() {
            let schema = DataValue::schema();
            assert!(matches!(schema, RefOr::T(Schema::Object(_))));
        }

        #[test]
        fn schemas_is_noop() {
            let mut collected: Vec<(std::string::String, RefOr<Schema>)> = Vec::new();
            DataValue::schemas(&mut collected);
            assert!(collected.is_empty());
        }
    }
}

impl std::fmt::Display for DataValue {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use DataValue::*;
        match self {
            Bool(i) => write!(f, "{}", i),
            U32(i) => write!(f, "{}", i),
            I32(i) => write!(f, "{}", i),
            U8(i) => write!(f, "{}", i),
            U64(i) => write!(f, "{}", i),
            I64(i) => write!(f, "{}", i),
            F32(i) => write!(f, "{}", i),
            F64(i) => write!(f, "{}", i),
            U128(i) => write!(f, "{}", i),
            I128(i) => write!(f, "{}", i),
            String(i) => write!(f, "{}", i),
            Bytes(i) => write!(f, "{}", std::string::String::from_utf8_lossy(i)),
            Null => write!(f, "null",),
            Vec(i) => write!(f, "{:?}", i),
            EnumNumber(i) => write!(f, "{:?}", i),
            Map(i) => write!(f, "{:?}", i),
        }
    }
}

impl DataValue {
    /// Extracts the value into a specific Rust type.
    ///
    /// This method uses the `Extract` trait to convert the `DataValue` into
    /// a native Rust type. Type conversion is handled automatically with sensible
    /// defaults for incompatible conversions.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use trs_data_value::DataValue;
    ///
    /// let value = DataValue::U32(42);
    /// let num: u32 = value.extract();
    /// assert_eq!(num, 42);
    ///
    /// let value = DataValue::String("hello".into());
    /// let text: String = value.extract();
    /// assert_eq!(text, "hello");
    /// ```
    pub fn extract<T>(&self) -> T
    where
        T: Extract,
    {
        T::extract(self)
    }

    /// Extracts bytes from a `DataValue::Bytes` variant.
    ///
    /// Returns the contained byte vector if this is a `Bytes` variant,
    /// otherwise returns an empty vector.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use trs_data_value::DataValue;
    ///
    /// let bytes = DataValue::Bytes(vec![1, 2, 3]);
    /// assert_eq!(bytes.extract_bytes(), vec![1, 2, 3]);
    ///
    /// let not_bytes = DataValue::U32(42);
    /// assert_eq!(not_bytes.extract_bytes(), Vec::<u8>::new());
    /// ```
    pub fn extract_bytes(&self) -> Vec<u8> {
        match self {
            DataValue::Bytes(i) => i.to_vec(),
            _ => vec![],
        }
    }
}

/// Trait for extracting native Rust types from `DataValue`.
///
/// This trait enables type-safe extraction of values from `DataValue` variants.
/// Implementations handle type conversion with sensible defaults when types don't match.
///
/// # Examples
///
/// ```rust
/// use trs_data_value::{DataValue, Extract};
///
/// let value = DataValue::I32(42);
/// let num = u32::extract(&value);
/// assert_eq!(num, 42);
/// ```
pub trait Extract {
    /// Extracts a value from a `DataValue` reference.
    fn extract(v: &DataValue) -> Self;
}

impl DataValue {
    /// Checks if the value contains a substring.
    ///
    /// Searches for the given string in:
    /// - String variants (substring search)
    /// - Vec variants (recursive search in each element)
    /// - Map variants (search in keys)
    ///
    /// Returns `false` for all other variant types.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use trs_data_value::DataValue;
    ///
    /// let text = DataValue::String("hello world".into());
    /// assert!(text.contains("world"));
    ///
    /// let vec = DataValue::Vec(vec![
    ///     DataValue::String("foo".into()),
    ///     DataValue::String("bar".into())
    /// ]);
    /// assert!(vec.contains("bar"));
    /// ```
    pub fn contains(&self, other: &str) -> bool {
        match self {
            DataValue::Vec(v) => v.iter().any(|x| x.contains(other)),
            DataValue::Map(v) => v.keys().any(|x| x.contains(other)),
            DataValue::String(v) => v.contains(other),
            _ => false,
        }
    }
}

/// Converts a slice of `DataValue` into a vector of a specific type.
///
/// This is a convenience function that extracts each `DataValue` in the slice
/// into the target type `D`.
///
/// # Examples
///
/// ```rust
/// use trs_data_value::{DataValue, convert};
///
/// let values = vec![
///     DataValue::U32(1),
///     DataValue::U32(2),
///     DataValue::U32(3),
/// ];
/// let numbers: Vec<u32> = convert(&values);
/// assert_eq!(numbers, vec![1, 2, 3]);
/// ```
pub fn convert<D: Extract>(data: &[DataValue]) -> Vec<D> {
    let mut result = Vec::with_capacity(data.len());
    for d in data {
        result.push(D::extract(d));
    }
    result
}

/// Macro for creating `DataValue` instances with convenient syntax.
///
/// This macro provides a JSON-like syntax for constructing complex `DataValue` structures.
///
/// # Syntax
///
/// - **Simple values**: `datavalue!(42)` - wraps a value
/// - **Vectors**: `datavalue!([1, 2, 3])` or `datavalue!(vec![1, 2, 3])`
/// - **Maps**: `datavalue!("key" => "value", "num" => 42)`
/// - **Nested structures**: Combine any of the above
///
/// # Examples
///
/// ```rust
/// use trs_data_value::datavalue;
///
/// // Simple value
/// let num = datavalue!(42);
///
/// // Vector
/// let vec = datavalue!([1, 2, 3]);
///
/// // Map with various types
/// let map = datavalue!(
///     "name" => "Alice",
///     "age" => 30,
///     "active" => true
/// );
///
/// // Map with array values
/// let complex = datavalue!(
///     "scores" => [95, 87, 92],
///     "tags" => ["rust", "programming"]
/// );
/// ```
#[macro_export]
macro_rules! datavalue {

     // case vec![1, 2, 3]
    ($(vec![$($value:expr),*]),*) => {
        $crate::datavalue!($([$($value),*]),*)
    };

    // case vec![1, 2, 3]
    ($([$($value:expr),*]),*) => {
        {
        $(
            let mut _vec = Vec::new();
            $(
                _vec.push($crate::DataValue::from($value));
            )*
            $crate::DataValue::Vec(_vec)
        )*
        }
    };
    // case { "a" => 1, }
    ($($key:expr => $value:expr,)+) => { $crate::datavalue!($($key => $value),+) };
    // case { "a" => vec![1, 2, 3] }
    ($($key:expr => vec![$($value:expr),*]),*) => {
        $crate::datavalue!($($key => [$($value),*]),*)
    };
    // case { "a" => [1, 2, 3] }
    ($($key:expr => [$($value:expr),*]),*) => {
        {
            let mut _map = ::std::collections::HashMap::new();
            $(
                let mut _arr = Vec::new();
                $(
                    _arr.push($value.into());
                )*
                _map.insert($key.into(), $crate::DataValue::Vec(_arr));
            )*
            $crate::DataValue::Map(_map)
        }
    };
    // case { "a" => 1 }
    ($($key:expr => $value:expr),*) => {
        {
            let mut _map = ::std::collections::HashMap::new();
            $(
                _map.insert($key.into(), $value.into());
            )*
            $crate::DataValue::Map(_map)
        }
    };
    // case simple value
    ($value:expr) => {
        $crate::DataValue::from($value)
    };
}
#[cfg(test)]
mod test {
    use std::fmt::Debug;

    use super::*;
    use rstest::*;

    #[test]
    fn from_json_object_to_datavalue() {
        let value = DataValue::from(&serde_json::json!(12));
        assert_eq!(value, DataValue::I64(12));

        let value = DataValue::from(&serde_json::json!(12.0));
        assert_eq!(value, DataValue::F64(12.0));

        let value = DataValue::from(&serde_json::json!("12"));
        assert_eq!(value, DataValue::String("12".into()));

        let value = DataValue::from(&serde_json::json!(true));
        assert_eq!(value, DataValue::Bool(true));

        let value = DataValue::from(&serde_json::json!(false));
        assert_eq!(value, DataValue::Bool(false));

        let value = DataValue::from(&serde_json::json!(null));
        assert_eq!(value, DataValue::Null);
    }

    #[test]
    fn from_native_to_datavalue() {
        let value = DataValue::from(12u32);
        assert_eq!(value, DataValue::U32(12));

        let value = DataValue::from(12.0f64);
        assert_eq!(value, DataValue::F64(12.0));

        let value = DataValue::from("12");
        assert_eq!(value, DataValue::String("12".into()));

        let value = DataValue::from(true);
        assert_eq!(value, DataValue::Bool(true));

        let value = DataValue::from(false);
        assert_eq!(value, DataValue::Bool(false));

        let value = DataValue::from(());
        assert_eq!(value, DataValue::Null);
    }

    #[rstest]
    #[case(DataValue::U32(12), 12u32)]
    #[case(DataValue::I32(12), 12i32)]
    #[case(DataValue::U8(12), 12u8)]
    #[case(DataValue::U64(12), 12u64)]
    #[case(DataValue::I64(12), 12i64)]
    #[case(DataValue::F32(12.0), 12f32)]
    #[case(DataValue::F64(12.0), 12f64)]
    #[case(DataValue::U128(12), 12u128)]
    #[case(DataValue::I128(12), 12i128)]
    #[case(DataValue::String("12".into()), "12".to_string())]
    #[case(DataValue::Bool(true), true)]
    #[case(DataValue::Bool(false), false)]
    fn as_data_value_test<T: PartialEq + std::fmt::Debug + Into<DataValue> + Extract>(
        #[case] value: DataValue,
        #[case] expected: T,
    ) {
        assert_eq!(value.extract::<T>(), expected);
        assert_eq!(value, expected.into());
    }

    #[rstest]
    #[case(DataValue::Bytes("12".as_bytes().to_vec()), "12".as_bytes().to_vec())]
    fn as_data_value_test_bytes(#[case] value: DataValue, #[case] expected: Vec<u8>) {
        assert_eq!(value.extract_bytes(), expected);
    }

    #[rstest]
    #[case(DataValue::U32(12), DataValue::U32(12), true)]
    #[case(DataValue::I32(12), DataValue::I32(12), true)]
    #[case(DataValue::U8(12), DataValue::U8(12), true)]
    #[case(DataValue::U64(12), DataValue::U64(12), true)]
    #[case(DataValue::I64(12), DataValue::I64(12), true)]
    #[case(DataValue::F32(12.0), DataValue::F32(12.0), true)]
    #[case(DataValue::F64(12.0), DataValue::F64(12.0), true)]
    #[case(DataValue::U128(12), DataValue::U128(12), true)]
    #[case(DataValue::I128(12), DataValue::I128(12), true)]
    #[case(DataValue::String("12".into()), DataValue::String("12".into()), true)]
    #[case(DataValue::Bytes("12".as_bytes().to_vec()), DataValue::Bytes("12".as_bytes().to_vec()), true)]
    #[case(DataValue::Bool(true), DataValue::Bool(true), true)]
    #[case(DataValue::Bool(false), DataValue::Bool(false), true)]
    fn eq_test(#[case] value: DataValue, #[case] other: DataValue, #[case] expected: bool) {
        assert_eq!(value == other, expected);
        print!("{} == {} = {}", value, other, expected);
    }

    #[rstest]
    #[case(
        crate::datavalue!(1u32),
        DataValue::U32(1),
        vec![1u32]
    )]
    #[case(
        crate::datavalue!(vec![1u32, 2u32]),
        DataValue::Vec(vec![DataValue::U32(1), DataValue::U32(2)]),
        vec![vec![1u32, 2u32]]
    )]
    fn convert_test<T: Extract + PartialEq + Debug>(
        #[case] value: DataValue,
        #[case] expected: DataValue,
        #[case] expected_native: Vec<T>,
    ) {
        assert_eq!(value, expected);
        let converted: Vec<T> = convert::<T>(&[value]);
        assert_eq!(converted, expected_native);
    }
}
