#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub struct Hstore(std::collections::BTreeMap<String, Option<String>>);

impl Hstore {
    pub fn new(value: std::collections::BTreeMap<String, Option<String>>) -> Self {
        Self(value)
    }

    pub fn empty() -> Self {
        Self::default()
    }

    pub fn from_entries<K, V, I>(entries: I) -> Self
    where
        K: Into<String>,
        V: Into<String>,
        I: IntoIterator<Item = (K, Option<V>)>,
    {
        entries.into_iter().collect()
    }

    pub fn value(&self) -> &std::collections::BTreeMap<String, Option<String>> {
        self.as_map()
    }

    pub fn as_map(&self) -> &std::collections::BTreeMap<String, Option<String>> {
        &self.0
    }

    pub fn into_inner(self) -> std::collections::BTreeMap<String, Option<String>> {
        self.0
    }

    pub fn to_wire(&self) -> &std::collections::BTreeMap<String, Option<String>> {
        self.as_map()
    }

    pub fn get(&self, key: &str) -> Option<Option<&str>> {
        self.0.get(key).map(|value| value.as_deref())
    }

    pub fn contains_key(&self, key: &str) -> bool {
        self.0.contains_key(key)
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }

    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub fn iter(&self) -> impl Iterator<Item = (&String, &Option<String>)> + '_ {
        self.0.iter()
    }

    pub fn insert<K, V>(&mut self, key: K, value: Option<V>) -> Option<Option<String>>
    where
        K: Into<String>,
        V: Into<String>,
    {
        self.0.insert(key.into(), value.map(Into::into))
    }

    pub fn with<K, V>(mut self, key: K, value: Option<V>) -> Self
    where
        K: Into<String>,
        V: Into<String>,
    {
        self.insert(key, value);
        self
    }

    pub fn with_null<K>(self, key: K) -> Self
    where
        K: Into<String>,
    {
        self.with(key, Option::<String>::None)
    }

    pub fn equals(&self, other: &Self) -> bool {
        self.as_map() == other.as_map()
    }
}

impl Default for Hstore {
    fn default() -> Self {
        Self(std::collections::BTreeMap::new())
    }
}

impl From<std::collections::BTreeMap<String, Option<String>>> for Hstore {
    fn from(value: std::collections::BTreeMap<String, Option<String>>) -> Self {
        Self::new(value)
    }
}

impl<K, V> std::iter::FromIterator<(K, Option<V>)> for Hstore
where
    K: Into<String>,
    V: Into<String>,
{
    fn from_iter<T: IntoIterator<Item = (K, Option<V>)>>(iter: T) -> Self {
        let values = iter
            .into_iter()
            .map(|(key, value)| (key.into(), value.map(Into::into)))
            .collect();
        Self::new(values)
    }
}

impl From<Hstore> for nautilus_core::Value {
    fn from(value: Hstore) -> Self {
        nautilus_core::Value::Hstore(value.into_inner())
    }
}

impl From<Option<Hstore>> for nautilus_core::Value {
    fn from(value: Option<Hstore>) -> Self {
        value.map(nautilus_core::Value::from).unwrap_or(nautilus_core::Value::Null)
    }
}

impl From<Vec<Hstore>> for nautilus_core::Value {
    fn from(values: Vec<Hstore>) -> Self {
        nautilus_core::Value::Array(values.into_iter().map(nautilus_core::Value::from).collect())
    }
}

impl nautilus_core::FromValue for Hstore {
    fn from_value(value: &nautilus_core::Value) -> nautilus_core::Result<Self> {
        <std::collections::BTreeMap<String, Option<String>> as nautilus_core::FromValue>::from_value(value)
            .map(Self::new)
    }

    fn from_value_owned(value: nautilus_core::Value) -> nautilus_core::Result<Self> {
        <std::collections::BTreeMap<String, Option<String>> as nautilus_core::FromValue>::from_value_owned(value)
            .map(Self::new)
    }
}

impl nautilus_core::FromValue for Vec<Hstore> {
    fn from_value(value: &nautilus_core::Value) -> nautilus_core::Result<Self> {
        <Vec<std::collections::BTreeMap<String, Option<String>>> as nautilus_core::FromValue>::from_value(value)
            .map(|values| values.into_iter().map(Self::new).collect())
    }
}
