use core::fmt::{self, Write};
use super::{Assign, IfNotExists, ListAppend, Math, Remove, Set, SetAction};
#[must_use = "Use in an update expression with `Update::from(set_remove)`"]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SetRemove {
    pub(crate) set: Option<Set>,
    pub(crate) remove: Option<Remove>,
}
impl SetRemove {
    pub fn and<T>(mut self, other: T) -> Self
    where
        T: Into<SetRemove>,
    {
        let other = other.into();
        if let Some(mut other) = other.set {
            self.set = match self.set {
                Some(mut current) => {
                    current.actions.append(&mut other.actions);
                    current
                }
                None => other,
            }
            .into();
        }
        if let Some(mut other) = other.remove {
            self.remove = match self.remove {
                Some(mut current) => {
                    current.paths.append(&mut other.paths);
                    current
                }
                None => other,
            }
            .into();
        }
        self
    }
}
impl fmt::Display for SetRemove {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match (&self.set, &self.remove) {
            (Some(set), Some(remove)) => {
                set.fmt(f)?;
                f.write_char(' ')?;
                remove.fmt(f)
            }
            (Some(set), None) => set.fmt(f),
            (None, Some(remove)) => remove.fmt(f),
            (None, None) => Ok(()),
        }
    }
}
impl From<Set> for SetRemove {
    fn from(set: Set) -> Self {
        Self {
            set: Some(set),
            remove: None,
        }
    }
}
impl From<SetAction> for SetRemove {
    fn from(value: SetAction) -> Self {
        Set::from(value).into()
    }
}
impl From<Assign> for SetRemove {
    fn from(value: Assign) -> Self {
        Set::from(value).into()
    }
}
impl From<Math> for SetRemove {
    fn from(value: Math) -> Self {
        Set::from(value).into()
    }
}
impl From<ListAppend> for SetRemove {
    fn from(value: ListAppend) -> Self {
        Set::from(value).into()
    }
}
impl From<IfNotExists> for SetRemove {
    fn from(value: IfNotExists) -> Self {
        Set::from(value).into()
    }
}
impl From<Remove> for SetRemove {
    fn from(remove: Remove) -> Self {
        Self {
            set: None,
            remove: Some(remove),
        }
    }
}
#[cfg(test)]
mod test {
    use std::error::Error;
    use pretty_assertions::assert_eq;
    use crate::{Num, Path};
    use super::*;
    #[test]
    fn display() -> Result<(), Box<dyn Error>> {
        let set = SetRemove::from("foo".parse::<Path>()?.set("a value"));
        assert_eq!(
            r#"SET foo = "a value""#,
            set.to_string(),
            "Should display only SET"
        );
        let remove = SetRemove::from("bar".parse::<Path>()?.remove());
        assert_eq!(
            "REMOVE bar",
            remove.to_string(),
            "Should display only REMOVE"
        );
        let set_remove = set.clone().and(remove.clone());
        assert_eq!(
            r#"SET foo = "a value" REMOVE bar"#,
            set_remove.to_string(),
            "Should display SET then REMOVE"
        );
        let remove_set = remove.and(set);
        assert_eq!(
            r#"SET foo = "a value" REMOVE bar"#,
            remove_set.to_string(),
            "Should display SET then REMOVE"
        );
        Ok(())
    }
    #[test]
    fn test_from_set() {
        let set_remove = SetRemove::from(Set::from("foo".parse::<Path>().unwrap().set("a value")));
        assert_eq!(r#"SET foo = "a value""#, set_remove.to_string());
    }
    #[test]
    fn test_from_set_action() {
        let set_remove = SetRemove::from(SetAction::from(
            "foo".parse::<Path>().unwrap().set("a value"),
        ));
        assert_eq!(r#"SET foo = "a value""#, set_remove.to_string());
    }
    #[test]
    fn test_from_assign() {
        let set_remove = SetRemove::from(Assign::new("foo".parse::<Path>().unwrap(), "a value"));
        assert_eq!(r#"SET foo = "a value""#, set_remove.to_string());
    }
    #[test]
    fn test_from_math() {
        let set_remove =
            SetRemove::from(Math::builder("foo".parse::<Path>().unwrap()).add(Num::from(1)));
        assert_eq!(r#"SET foo = foo + 1"#, set_remove.to_string());
    }
    #[test]
    fn test_from_list_append() {
        let set_remove = SetRemove::from(
            ListAppend::builder("foo".parse::<Path>().unwrap()).list(["a", "b", "c"]),
        );
        assert_eq!(
            r#"SET foo = list_append(foo, ["a", "b", "c"])"#,
            set_remove.to_string()
        );
    }
    #[test]
    fn test_from_if_not_exists() {
        let set_remove =
            SetRemove::from(IfNotExists::builder("foo".parse::<Path>().unwrap()).set("a value"));
        assert_eq!(
            r#"SET foo = if_not_exists(foo, "a value")"#,
            set_remove.to_string()
        );
    }
    #[test]
    fn test_from_remove() {
        let set_remove = SetRemove::from("foo".parse::<Path>().unwrap().remove());
        assert_eq!("REMOVE foo", set_remove.to_string());
    }
}