//! Conversion implementations from various types to `DataValue`.
//!
//! This module provides `From` trait implementations for converting native Rust types,
//! JSON values, Protocol Buffer values, and collections into `DataValue`.
//!
//! # Examples
//!
//! ```rust
//! use trs_data_value::DataValue;
//!
//! // From native types
//! let num: DataValue = 42u32.into();
//! let text: DataValue = "hello".into();
//! let flag: DataValue = true.into();
//!
//! // From collections
//! let vec: DataValue = vec![1usize, 2, 3].into();
//!
//! // From JSON
//! let json = serde_json::json!({"key": "value"});
//! let data: DataValue = json.into();
//! ```

use std::collections::HashMap;

use super::DataValue;

impl From<()> for DataValue {
    fn from(_value: ()) -> Self {
        DataValue::Null
    }
}

impl From<u32> for DataValue {
    fn from(value: u32) -> Self {
        DataValue::U32(value)
    }
}
impl From<usize> for DataValue {
    fn from(value: usize) -> Self {
        DataValue::U32(value as u32)
    }
}
impl From<i32> for DataValue {
    fn from(value: i32) -> Self {
        DataValue::I32(value)
    }
}
impl From<u8> for DataValue {
    fn from(value: u8) -> Self {
        DataValue::U8(value)
    }
}
impl From<u64> for DataValue {
    fn from(value: u64) -> Self {
        DataValue::U64(value)
    }
}
impl From<i64> for DataValue {
    fn from(value: i64) -> Self {
        DataValue::I64(value)
    }
}
impl From<f32> for DataValue {
    fn from(value: f32) -> Self {
        DataValue::F32(value)
    }
}
impl From<u128> for DataValue {
    fn from(value: u128) -> Self {
        DataValue::U128(value)
    }
}
impl From<i128> for DataValue {
    fn from(value: i128) -> Self {
        DataValue::I128(value)
    }
}
impl From<f64> for DataValue {
    fn from(value: f64) -> Self {
        DataValue::F64(value)
    }
}
impl From<String> for DataValue {
    fn from(value: String) -> Self {
        DataValue::String(value.into())
    }
}

impl From<smartstring::alias::String> for DataValue {
    fn from(value: smartstring::alias::String) -> Self {
        DataValue::String(value)
    }
}

impl From<&str> for DataValue {
    fn from(value: &str) -> Self {
        DataValue::String(value.into())
    }
}

impl From<Vec<u8>> for DataValue {
    fn from(value: Vec<u8>) -> Self {
        DataValue::Bytes(value)
    }
}
impl From<bool> for DataValue {
    fn from(value: bool) -> Self {
        DataValue::Bool(value)
    }
}
impl From<Option<String>> for DataValue {
    fn from(value: Option<String>) -> Self {
        match value {
            Some(value) => DataValue::String(value.into()),
            None => DataValue::Null,
        }
    }
}

impl From<Option<smartstring::alias::String>> for DataValue {
    fn from(value: Option<smartstring::alias::String>) -> Self {
        match value {
            Some(value) => DataValue::String(value),
            None => DataValue::Null,
        }
    }
}
impl From<Option<&str>> for DataValue {
    fn from(value: Option<&str>) -> Self {
        DataValue::from(value.map(|x| x.to_string()))
    }
}
impl From<Option<&String>> for DataValue {
    fn from(value: Option<&String>) -> Self {
        DataValue::from(value.cloned())
    }
}
impl From<Option<&smartstring::alias::String>> for DataValue {
    fn from(value: Option<&smartstring::alias::String>) -> Self {
        DataValue::from(value.cloned())
    }
}
impl From<Vec<DataValue>> for DataValue {
    fn from(value: Vec<DataValue>) -> Self {
        DataValue::Vec(value)
    }
}

impl From<Vec<String>> for DataValue {
    fn from(value: Vec<String>) -> Self {
        DataValue::Vec(value.into_iter().map(|x| x.into()).collect())
    }
}

impl From<&[u8]> for DataValue {
    fn from(value: &[u8]) -> Self {
        DataValue::Bytes(value.to_vec())
    }
}

impl From<Vec<smartstring::alias::String>> for DataValue {
    fn from(value: Vec<smartstring::alias::String>) -> Self {
        DataValue::Vec(value.into_iter().map(|x| x.into()).collect())
    }
}
impl From<Vec<usize>> for DataValue {
    fn from(value: Vec<usize>) -> Self {
        DataValue::Vec(value.into_iter().map(|x| (x as u32).into()).collect())
    }
}
impl From<HashMap<String, DataValue>> for DataValue {
    fn from(value: HashMap<String, DataValue>) -> Self {
        DataValue::Map(value.into_iter().map(|(k, v)| (k.into(), v)).collect())
    }
}

impl From<HashMap<smartstring::alias::String, DataValue>> for DataValue {
    fn from(value: HashMap<smartstring::alias::String, DataValue>) -> Self {
        DataValue::Map(value)
    }
}

impl From<&prost_reflect::Value> for DataValue {
    fn from(proto_value: &prost_reflect::Value) -> Self {
        match proto_value {
            prost_reflect::Value::Bool(b) => DataValue::Bool(*b),
            prost_reflect::Value::I32(b) => DataValue::I32(*b),
            prost_reflect::Value::I64(b) => DataValue::I64(*b),
            prost_reflect::Value::U32(b) => DataValue::U32(*b),
            prost_reflect::Value::U64(b) => DataValue::U64(*b),
            prost_reflect::Value::F32(b) => DataValue::F32(*b),
            prost_reflect::Value::F64(b) => DataValue::F64(*b),
            prost_reflect::Value::String(b) => DataValue::String(b.into()),
            prost_reflect::Value::Bytes(b) => DataValue::Bytes(b.to_vec()),
            prost_reflect::Value::EnumNumber(b) => DataValue::EnumNumber(*b),
            prost_reflect::Value::List(b) => DataValue::Vec(b.iter().map(|x| x.into()).collect()),
            prost_reflect::Value::Map(b) => DataValue::Map(
                b.iter()
                    .map(|(k, v)| {
                        let key = match k {
                            prost_reflect::MapKey::String(k) => k.clone(),
                            prost_reflect::MapKey::U32(k) => k.to_string(),
                            prost_reflect::MapKey::U64(k) => k.to_string(),
                            prost_reflect::MapKey::I32(k) => k.to_string(),
                            prost_reflect::MapKey::I64(k) => k.to_string(),
                            prost_reflect::MapKey::Bool(k) => k.to_string(),
                        };
                        (key.into(), DataValue::from(v))
                    })
                    .collect::<HashMap<_, _>>(),
            ),
            e => {
                tracing::trace!("e: {:?}", e);
                DataValue::Null
            } /*prost_reflect::Value::Message(b) => {
                  DataValue::Map(b.iter_fields().map(|(k, v)| (k.into(), v.into())).collect())
              }*/
        }
    }
}
impl From<prost_reflect::Value> for DataValue {
    fn from(proto_value: prost_reflect::Value) -> Self {
        DataValue::from(&proto_value)
    }
}
impl From<&serde_json::Value> for DataValue {
    fn from(proto_value: &serde_json::Value) -> Self {
        match proto_value {
            serde_json::Value::Bool(b) => DataValue::Bool(*b),
            serde_json::Value::Number(b) => {
                if b.is_i64() {
                    DataValue::I64(b.as_i64().expect("BUG: should be i64"))
                } else if b.is_u64() {
                    DataValue::U64(b.as_u64().expect("BUG: should be u64"))
                } else {
                    DataValue::F64(b.as_f64().expect("BUG: should be f64"))
                }
            }
            serde_json::Value::String(b) => DataValue::String(b.into()),
            serde_json::Value::Null => DataValue::Null,
            serde_json::Value::Array(v) => DataValue::Vec(v.iter().map(DataValue::from).collect()),
            serde_json::Value::Object(v) => DataValue::Map(
                v.iter()
                    .map(|(k, v)| (k.clone().into(), DataValue::from(v)))
                    .collect(),
            ),
        }
    }
}
impl From<serde_json::Value> for DataValue {
    fn from(proto_value: serde_json::Value) -> Self {
        DataValue::from(&proto_value)
    }
}

use base64::{
    alphabet,
    engine::{self, general_purpose},
    Engine as _,
};
const CUSTOM_ENGINE: engine::GeneralPurpose =
    engine::GeneralPurpose::new(&alphabet::URL_SAFE, general_purpose::NO_PAD);
use serde_json::json;

impl DataValue {
    /// Converts this `DataValue` into a `serde_json::Value`.
    ///
    /// Bytes are base64-encoded in the resulting JSON.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use trs_data_value::DataValue;
    /// use serde_json::json;
    ///
    /// let value = DataValue::U32(42);
    /// assert_eq!(value.to_json(), json!(42));
    ///
    /// let value = DataValue::String("hello".into());
    /// assert_eq!(value.to_json(), json!("hello"));
    /// ```
    #[inline]
    pub fn to_json(self) -> serde_json::Value {
        match self {
            DataValue::Bool(b) => serde_json::Value::Bool(b),
            DataValue::U32(b) => serde_json::Value::Number(b.into()),
            DataValue::I32(b) => serde_json::Value::Number(b.into()),
            DataValue::U8(b) => serde_json::Value::Number(b.into()),
            DataValue::U64(b) => serde_json::Value::Number(b.into()),
            DataValue::I64(b) => serde_json::Value::Number(b.into()),
            DataValue::F32(b) => json!(b),
            DataValue::F64(b) => json!(b),
            DataValue::U128(b) => json!(b),
            DataValue::I128(b) => json!(b),
            DataValue::String(b) => serde_json::Value::String(b.into()),
            DataValue::Bytes(b) => serde_json::Value::String(CUSTOM_ENGINE.encode(b)),
            DataValue::Null => serde_json::Value::Null,
            DataValue::Vec(b) => {
                serde_json::Value::Array(b.into_iter().map(|x| x.to_json()).collect())
            }
            DataValue::Map(b) => serde_json::Value::Object(
                b.into_iter()
                    .map(|(k, v)| (k.to_string(), v.to_json()))
                    .collect(),
            ),
            DataValue::EnumNumber(b) => serde_json::Value::Number(b.into()),
        }
    }
}

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

    #[rstest]
    fn data_value_from() {
        assert_eq!(
            DataValue::String("key".into()),
            DataValue::from(Some("key"))
        );
        assert_eq!(
            DataValue::String("key".into()),
            DataValue::from(Some("key".to_string()))
        );
        assert_eq!(
            DataValue::String("key".into()),
            DataValue::from(Some(smartstring::alias::String::from("key")))
        );
        assert_eq!(
            DataValue::String("key".into()),
            DataValue::from(Some(&smartstring::alias::String::from("key")))
        );

        assert_eq!(
            DataValue::Vec(vec![DataValue::String("key".into())]),
            DataValue::Vec(vec![DataValue::from(Some("key"))])
        );
        assert_eq!(
            DataValue::Vec(vec![DataValue::String("key".into())]),
            DataValue::Vec(vec![DataValue::from(smartstring::alias::String::from(
                "key"
            ))])
        );

        let map: HashMap<String, DataValue> =
            crate::stdhashmap!(String::from("key") => DataValue::String("key".into()));
        assert_eq!(
            DataValue::from(map),
            DataValue::Map(crate::stdhashmap!("key" => DataValue::from("key")))
        );
        let map: HashMap<smartstring::alias::String, DataValue> = crate::stdhashmap!(smartstring::alias::String::from("key") => DataValue::String("key".into()));
        assert_eq!(
            DataValue::from(map),
            DataValue::Map(crate::stdhashmap!("key" => DataValue::from("key")))
        );
    }

    #[rstest]
    #[case(DataValue::U32(12), serde_json::json!(12))]
    #[case(DataValue::I32(12), serde_json::json!(12))]
    #[case(DataValue::I64(12), serde_json::json!(12))]
    #[case(DataValue::U64(12), serde_json::json!(12))]
    #[case(DataValue::F32(12f32), serde_json::json!(12f32))]
    #[case(DataValue::F64(12f64), serde_json::json!(12f64))]
    #[case(DataValue::U8(12), serde_json::json!(12))]
    #[case(DataValue::U128(12), serde_json::json!(12))]
    #[case(DataValue::I128(12), serde_json::json!(12))]
    #[case(DataValue::Bool(true), serde_json::json!(true))]
    #[case(DataValue::Null, serde_json::json!(null))]
    #[case(DataValue::Vec(vec![1.into(),2.into(),3.into(),4.into()]), serde_json::json!(vec![1,2,3,4]))]
    #[case(DataValue::String("test".into()), serde_json::json!("test"))]
    #[case(DataValue::Map(crate::stdhashmap!("key" => "test")), serde_json::json!({"key": "test"}))]
    #[case(DataValue::Bytes(vec![1,2,3,4]), serde_json::json!("AQIDBA"))]
    fn from_json(#[case] input: DataValue, #[case] expected: serde_json::Value) {
        assert_eq!(input.to_json(), expected);
    }

    #[rstest]
    #[case(DataValue::I64(i64::MAX), serde_json::json!(i64::MAX))]
    #[case(DataValue::I64(i32::MAX as i64), serde_json::json!(i32::MAX))]
    #[case(DataValue::F64(f32::MAX as f64), serde_json::json!(f32::MAX))]
    #[case(DataValue::F64(f64::MAX), serde_json::json!(f64::MAX))]
    #[case(DataValue::Bool(true), serde_json::json!(true))]
    #[case(DataValue::Null, serde_json::json!(null))]
    #[case(DataValue::Vec(vec![1i64.into(),2i64.into(),3i64.into(),4i64.into()]), serde_json::json!(vec![1,2,3,4]))]
    #[case(DataValue::String("test".into()), serde_json::json!("test"))]
    #[case(DataValue::Map(crate::stdhashmap!("key" => "test")), serde_json::json!({"key": "test"}))]
    fn to_json(#[case] expected: DataValue, #[case] input: serde_json::Value) {
        assert_eq!(DataValue::from(&input), expected);
    }

    #[rstest]
    #[case(DataValue::I64(i64::MAX), prost_reflect::Value::I64(i64::MAX))]
    #[case(DataValue::I32(i32::MAX), prost_reflect::Value::I32(i32::MAX))]
    #[case(DataValue::F32(f32::MAX), prost_reflect::Value::F32(f32::MAX))]
    #[case(DataValue::F64(f64::MAX), prost_reflect::Value::F64(f64::MAX))]
    #[case(DataValue::Bool(true), prost_reflect::Value::Bool(true))]
    #[case(DataValue::Vec(vec![1i64.into(),2i64.into(),3i64.into(),4i64.into()]), prost_reflect::Value::List(vec![prost_reflect::Value::I64(1),prost_reflect::Value::I64(2),prost_reflect::Value::I64(3),prost_reflect::Value::I64(4)]))]
    #[case(DataValue::String("test".into()), prost_reflect::Value::String("test".to_string()))]
    #[case(DataValue::Map(crate::stdhashmap!("key" => "test")), prost_reflect::Value::Map(crate::stdhashmap!(prost_reflect::MapKey::String("key".to_string()) => prost_reflect::Value::String("test".to_string()))))]
    fn from_prost(#[case] expected: DataValue, #[case] input: prost_reflect::Value) {
        assert_eq!(DataValue::from(&input), expected);
    }
}
