use crate::storage::Error;
use oauth2_client::re_exports::{Deserialize, Serialize};
use pbbson::{Model, prost::Message};

pub fn is_audit_field(key: &str) -> bool {
    key.starts_with("created") || key.starts_with("deleted") || key.starts_with("updated")
}

pub fn is_equal_ignoring_audit_fields(left: &Model, right: &Model) -> bool {
    for key in left.keys() {
        if is_audit_field(key) {
            continue;
        }
        if left.get(key) != right.get(key) {
            return false;
        }
    }

    for key in right.keys() {
        if is_audit_field(key) {
            continue;
        }
        if left.get(key) != right.get(key) {
            return false;
        }
    }

    true
}

pub fn is_message_equal_ignoring_audit_fields<T: Message + Clone + Default + for<'a> Deserialize<'a> + Serialize>(
    left: &T,
    right: &T,
) -> Result<bool, Error> {
    let left = Model::try_from(left)?;
    let right = Model::try_from(right)?;
    Ok(super::is_equal_ignoring_audit_fields(&left, &right))
}

#[cfg(test)]
mod tests {
    use crate::models::is_equal_ignoring_audit_fields;
    use pbbson::Model;
    use pbbson::bson::{DateTime, doc};

    #[test]
    fn can_compare_with_identity() {
        let left = Model::from(doc! {"name": "Joe", "age": 23_u32});
        let right = left.clone();
        assert!(is_equal_ignoring_audit_fields(&left, &right));
    }

    #[test]
    fn can_detect_equality_when_audit_fields_dont_match() {
        let left = Model::from(doc! {"name": "Joe"});
        let now = DateTime::now();
        let right = Model::from(doc! {
            "createdAt": now,
            "name": "Joe",
        });
        assert!(is_equal_ignoring_audit_fields(&left, &right));
    }

    #[test]
    fn can_detect_inequality_when_audit_fields_match() {
        let now = DateTime::now();
        let left = Model::from(doc! {
            "createdAt": now,
            "name": "Joe",
        });
        let right = Model::from(doc! {
            "createdAt": now,
            "name": "Jane",
        });
        assert!(!is_equal_ignoring_audit_fields(&left, &right));
    }
}
