//! Types for Matrix member hint state events ([MSC4171]).
//!
//! This implements `m.member_hints` state event described in [MSC4171].
//!
//! [MSC4171]: https://github.com/matrix-org/matrix-spec-proposals/pull/4171

use std::collections::BTreeSet;

use ruma_common::OwnedUserId;
use ruma_macros::EventContent;
use serde::{Deserialize, Serialize};

use crate::EmptyStateKey;

/// The content for an `m.member_hints` state event.
///
/// Any users (service members) listed in the content should not be considered when computing the
/// room name or avatar based on the member list.
#[derive(Clone, Debug, Default, Serialize, Deserialize, EventContent, PartialEq)]
#[ruma_event(type = "io.element.functional_members", kind = State, state_key_type = EmptyStateKey)]
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
pub struct MemberHintsEventContent {
    /// The list of user IDs that should be considered a service member of the room.
    pub service_members: BTreeSet<OwnedUserId>,
}

impl MemberHintsEventContent {
    /// Create a new [`MemberHintsEventContent`] with the given set of service members.
    pub fn new(service_members: BTreeSet<OwnedUserId>) -> Self {
        Self { service_members }
    }
}

#[cfg(test)]
mod test {
    use std::collections::BTreeSet;

    use assert_matches2::assert_matches;
    use ruma_common::user_id;
    use serde_json::{from_value as from_json_value, json};

    use super::*;
    use crate::AnyStateEvent;

    #[test]
    fn deserialize() {
        let user_id = user_id!("@slackbot:matrix.org");

        let data = json!({
            "type": "io.element.functional_members",
            "state_key": "",
            "content": {
                "service_members": [
                    user_id,
                ]
            },
            "origin_server_ts": 111,
            "event_id": "$3qfxjGYSu4sL25FtR0ep6vePOc",
            "room_id": "!1234:example.org",
            "sender": "@user:example.org"
        });

        let event = from_json_value::<AnyStateEvent>(data)
            .expect("We should be able to deserialize the member hints event");

        assert_matches!(event, AnyStateEvent::MemberHints(event));
        assert_matches!(event, crate::StateEvent::Original(event));

        assert!(event.content.service_members.contains(user_id));

        let data = json!({
            "type": "m.member_hints",
            "state_key": "",
            "content": {
                "service_members": [
                    user_id,
                ]
            },
            "origin_server_ts": 111,
            "event_id": "$3qfxjGYSu4sL25FtR0ep6vePOc",
            "room_id": "!1234:example.org",
            "sender": "@user:example.org"
        });

        let event = from_json_value::<AnyStateEvent>(data)
            .expect("We should be able to deserialize the member hints event");

        assert_matches!(event, AnyStateEvent::MemberHints(event));
        assert_matches!(event, crate::StateEvent::Original(event));

        assert!(event.content.service_members.contains(user_id));
    }

    #[test]
    fn serialize() {
        let user_id = user_id!("@slackbot:matrix.org");
        let content = MemberHintsEventContent::new(BTreeSet::from([user_id.to_owned()]));

        let serialized = serde_json::to_value(content)
            .expect("We should be able to serialize the member hints content");

        let expected = json!({
            "service_members": [
                user_id,
            ]
        });

        assert_eq!(
            expected, serialized,
            "The serialized member hints content should match the expected one"
        );
    }
}
