from collections.abc import Iterable
from datetime import datetime, timedelta
from unittest import mock

import orjson
import time_machine
from django.utils.timezone import now as timezone_now

from zerver.actions.create_realm import do_create_realm
from zerver.actions.create_user import do_reactivate_user
from zerver.actions.realm_settings import (
    do_change_realm_permission_group_setting,
    do_change_realm_plan_type,
    do_set_realm_property,
)
from zerver.actions.streams import (
    do_change_stream_group_based_setting,
    do_deactivate_stream,
    do_unarchive_stream,
)
from zerver.actions.user_groups import (
    add_subgroups_to_user_group,
    bulk_add_members_to_user_groups,
    bulk_remove_members_from_user_groups,
    check_add_user_group,
    create_user_group_in_database,
    do_change_user_group_permission_setting,
    do_deactivate_user_group,
    promote_new_full_members,
    remove_subgroups_from_user_group,
)
from zerver.actions.users import do_deactivate_user
from zerver.lib.create_user import create_user
from zerver.lib.exceptions import JsonableError
from zerver.lib.mention import silent_mention_syntax_for_user
from zerver.lib.streams import ensure_stream
from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.test_helpers import most_recent_usermessage
from zerver.lib.timestamp import datetime_to_timestamp
from zerver.lib.types import UserGroupMembersData, UserGroupMembersDict
from zerver.lib.user_groups import (
    check_user_has_permission_by_role,
    get_direct_user_groups,
    get_recursive_group_members,
    get_recursive_group_members_union_for_groups,
    get_recursive_membership_groups,
    get_recursive_strict_subgroups,
    get_recursive_subgroups,
    get_recursive_subgroups_union_for_groups,
    get_recursive_supergroups_union_for_groups,
    get_role_based_system_groups_dict,
    get_subgroup_ids,
    get_system_user_group_by_name,
    get_user_group_member_ids,
    has_user_group_access_for_subgroup,
    is_any_user_in_group,
    is_user_in_group,
    user_group_ids_to_user_groups,
    user_groups_in_realm_serialized,
)
from zerver.models import (
    GroupGroupMembership,
    NamedUserGroup,
    Realm,
    Stream,
    UserGroup,
    UserGroupMembership,
    UserProfile,
)
from zerver.models.groups import SystemGroups, get_realm_system_groups_name_dict
from zerver.models.realms import get_realm


class UserGroupTestCase(ZulipTestCase):
    def assert_user_membership(
        self, user_group: NamedUserGroup, members: Iterable[UserProfile]
    ) -> None:
        user_ids = get_user_group_member_ids(user_group, direct_member_only=True)
        self.assertSetEqual(set(user_ids), {member.id for member in members})

    def assert_member_not_in_group(self, user_group: NamedUserGroup, member: UserProfile) -> None:
        user_ids = get_user_group_member_ids(user_group, direct_member_only=True)
        self.assertNotIn(member.id, user_ids)

    def assert_subgroup_membership(
        self, user_group: NamedUserGroup, members: Iterable[UserGroup]
    ) -> None:
        subgroup_ids = get_subgroup_ids(user_group, direct_subgroup_only=True)
        self.assertSetEqual(set(subgroup_ids), {member.id for member in members})

    def create_user_group_for_test(
        self, group_name: str, acting_user: UserProfile
    ) -> NamedUserGroup:
        members = [self.example_user("othello")]
        return check_add_user_group(
            get_realm("zulip"), group_name, members, acting_user=acting_user
        )

    def test_user_groups_in_realm_serialized(self) -> None:
        def convert_date_created_to_timestamp(date_created: datetime | None) -> int | None:
            return datetime_to_timestamp(date_created) if date_created is not None else None

        realm = get_realm("zulip")
        user = self.example_user("iago")
        user_group = NamedUserGroup.objects.filter(realm_for_sharding=realm).first()
        assert user_group is not None
        empty_user_group = check_add_user_group(realm, "newgroup", [], acting_user=user)
        do_deactivate_user(self.example_user("hamlet"), acting_user=None)

        user_groups = user_groups_in_realm_serialized(
            realm, include_deactivated_groups=False
        ).api_groups
        self.assert_length(user_groups, 10)
        self.assertEqual(user_groups[0]["id"], user_group.id)
        self.assertEqual(user_groups[0]["creator_id"], user_group.creator_id)
        self.assertEqual(
            user_groups[0]["date_created"],
            convert_date_created_to_timestamp(user_group.date_created),
        )
        self.assertEqual(user_groups[0]["name"], SystemGroups.NOBODY)
        self.assertEqual(user_groups[0]["description"], "Nobody")
        self.assertEqual(user_groups[0]["members"], [])
        self.assertEqual(user_groups[0]["direct_subgroup_ids"], [])
        self.assertEqual(user_groups[0]["can_manage_group"], user_group.id)
        self.assertEqual(user_groups[0]["can_mention_group"], user_group.id)
        self.assertFalse(user_groups[0]["deactivated"])

        owners_system_group = NamedUserGroup.objects.get(
            name=SystemGroups.OWNERS, realm_for_sharding=realm
        )
        membership = UserGroupMembership.objects.filter(user_group=owners_system_group).values_list(
            "user_profile_id", flat=True
        )
        self.assertEqual(user_groups[1]["id"], owners_system_group.id)
        self.assertEqual(user_groups[1]["creator_id"], owners_system_group.creator_id)
        self.assertEqual(
            user_groups[1]["date_created"],
            convert_date_created_to_timestamp(owners_system_group.date_created),
        )
        self.assertEqual(user_groups[1]["name"], SystemGroups.OWNERS)
        self.assertEqual(user_groups[1]["description"], "Owners of this organization")
        self.assertEqual(set(user_groups[1]["members"]), set(membership))
        self.assertEqual(user_groups[1]["direct_subgroup_ids"], [])
        self.assertEqual(user_groups[1]["can_manage_group"], user_group.id)
        self.assertEqual(user_groups[1]["can_mention_group"], user_group.id)
        self.assertFalse(user_groups[0]["deactivated"])

        admins_system_group = NamedUserGroup.objects.get(
            name=SystemGroups.ADMINISTRATORS, realm_for_sharding=realm
        )
        self.assertEqual(user_groups[2]["id"], admins_system_group.id)
        # Check that owners system group is present in "direct_subgroup_ids"
        self.assertEqual(user_groups[2]["direct_subgroup_ids"], [owners_system_group.id])

        self.assertEqual(user_groups[8]["name"], "hamletcharacters")
        # Test deactivated user is not included in the members list.
        self.assertEqual(user_groups[8]["members"], [self.example_user("cordelia").id])

        everyone_group = NamedUserGroup.objects.get(
            name=SystemGroups.EVERYONE, realm_for_sharding=realm, is_system_group=True
        )
        self.assertEqual(user_groups[9]["id"], empty_user_group.id)
        self.assertEqual(user_groups[9]["creator_id"], empty_user_group.creator_id)
        self.assertEqual(
            user_groups[9]["date_created"],
            convert_date_created_to_timestamp(empty_user_group.date_created),
        )
        self.assertEqual(user_groups[9]["name"], "newgroup")
        self.assertEqual(user_groups[9]["description"], "")
        self.assertEqual(user_groups[9]["members"], [])
        self.assertEqual(
            user_groups[9]["can_manage_group"],
            UserGroupMembersDict(direct_members=[11], direct_subgroups=[]),
        )
        self.assertEqual(user_groups[9]["can_mention_group"], everyone_group.id)
        self.assertFalse(user_groups[0]["deactivated"])

        othello = self.example_user("othello")
        hamletcharacters_group = NamedUserGroup.objects.get(
            name="hamletcharacters", realm_for_sharding=realm
        )
        setting_group = self.create_or_update_anonymous_group_for_setting(
            [othello], [admins_system_group, hamletcharacters_group]
        )
        new_user_group = check_add_user_group(
            realm,
            "newgroup2",
            [othello],
            group_settings_map={
                "can_manage_group": setting_group,
                "can_mention_group": setting_group,
            },
            acting_user=self.example_user("desdemona"),
        )
        user_groups = user_groups_in_realm_serialized(
            realm, include_deactivated_groups=False
        ).api_groups
        self.assertEqual(user_groups[10]["id"], new_user_group.id)
        self.assertEqual(user_groups[10]["creator_id"], new_user_group.creator_id)
        self.assertEqual(
            user_groups[10]["date_created"],
            convert_date_created_to_timestamp(new_user_group.date_created),
        )
        self.assertEqual(user_groups[10]["name"], "newgroup2")
        self.assertEqual(user_groups[10]["description"], "")
        self.assertEqual(user_groups[10]["members"], [othello.id])

        assert not isinstance(user_groups[10]["can_manage_group"], int)
        self.assertEqual(user_groups[10]["can_manage_group"]["direct_members"], [othello.id])
        self.assertCountEqual(
            user_groups[10]["can_manage_group"]["direct_subgroups"],
            [admins_system_group.id, hamletcharacters_group.id],
        )

        assert not isinstance(user_groups[10]["can_mention_group"], int)
        self.assertEqual(user_groups[10]["can_mention_group"]["direct_members"], [othello.id])
        self.assertCountEqual(
            user_groups[10]["can_mention_group"]["direct_subgroups"],
            [admins_system_group.id, hamletcharacters_group.id],
        )
        self.assertFalse(user_groups[0]["deactivated"])

        hamlet = self.example_user("hamlet")
        another_new_group = check_add_user_group(realm, "newgroup3", [hamlet], acting_user=hamlet)
        add_subgroups_to_user_group(
            new_user_group, [another_new_group, owners_system_group], acting_user=None
        )
        do_deactivate_user_group(another_new_group, acting_user=None)
        user_groups = user_groups_in_realm_serialized(
            realm, include_deactivated_groups=True
        ).api_groups
        self.assert_length(user_groups, 12)
        self.assertEqual(user_groups[10]["id"], new_user_group.id)
        self.assertEqual(user_groups[10]["name"], "newgroup2")
        self.assertFalse(user_groups[10]["deactivated"])
        self.assertCountEqual(
            user_groups[10]["direct_subgroup_ids"], [another_new_group.id, owners_system_group.id]
        )
        self.assertEqual(user_groups[11]["id"], another_new_group.id)
        self.assertEqual(user_groups[11]["name"], "newgroup3")
        self.assertTrue(user_groups[11]["deactivated"])

        do_change_realm_permission_group_setting(
            realm, "create_multiuse_invite_group", hamletcharacters_group, acting_user=None
        )
        cordelia = self.example_user("cordelia")
        setting_group = self.create_or_update_anonymous_group_for_setting(
            [cordelia], [owners_system_group]
        )
        do_change_realm_permission_group_setting(
            realm, "can_create_public_channel_group", setting_group, acting_user=None
        )
        realm_user_groups = user_groups_in_realm_serialized(
            realm, include_deactivated_groups=False, fetch_anonymous_group_membership=True
        )
        named_user_groups = realm_user_groups.api_groups
        self.assert_length(named_user_groups, 11)
        self.assertEqual(named_user_groups[10]["id"], new_user_group.id)
        self.assertEqual(named_user_groups[10]["name"], "newgroup2")
        self.assertFalse(named_user_groups[10]["deactivated"])
        self.assertCountEqual(
            named_user_groups[10]["direct_subgroup_ids"],
            [another_new_group.id, owners_system_group.id],
        )

        system_groups_dict = realm_user_groups.system_groups_name_dict
        self.assertEqual(system_groups_dict[user_group.id], SystemGroups.NOBODY)
        self.assertEqual(system_groups_dict[owners_system_group.id], SystemGroups.OWNERS)
        self.assertEqual(system_groups_dict[admins_system_group.id], SystemGroups.ADMINISTRATORS)
        self.assertEqual(system_groups_dict[everyone_group.id], SystemGroups.EVERYONE)

        anonymous_group_membership = realm_user_groups.anonymous_group_membership
        self.assertIn(realm.can_create_public_channel_group_id, anonymous_group_membership)
        self.assertEqual(
            anonymous_group_membership[realm.can_create_public_channel_group_id],
            UserGroupMembersDict(
                direct_members=[cordelia.id], direct_subgroups=[owners_system_group.id]
            ),
        )
        self.assertNotIn(realm.create_multiuse_invite_group_id, anonymous_group_membership)

    def test_get_direct_user_groups(self) -> None:
        othello = self.example_user("othello")
        self.create_user_group_for_test("support", acting_user=self.example_user("desdemona"))
        user_groups = get_direct_user_groups(othello)
        self.assert_length(user_groups, 3)
        # othello is a direct member of two role-based system groups also.
        user_group_names = [group.named_user_group.name for group in user_groups]
        self.assertEqual(
            set(user_group_names),
            {"support", SystemGroups.MEMBERS, SystemGroups.FULL_MEMBERS},
        )

    def test_recursive_queries_for_user_groups(self) -> None:
        realm = get_realm("zulip")
        iago = self.example_user("iago")
        desdemona = self.example_user("desdemona")
        shiva = self.example_user("shiva")
        aaron = self.example_user("aaron")
        prospero = self.example_user("prospero")

        leadership_group = check_add_user_group(
            realm, "Leadership", [desdemona], acting_user=desdemona
        )

        staff_group = check_add_user_group(realm, "Staff", [iago], acting_user=iago)
        GroupGroupMembership.objects.create(supergroup=staff_group, subgroup=leadership_group)

        manager_group = check_add_user_group(
            realm, "Managers", [aaron, prospero], acting_user=aaron
        )
        GroupGroupMembership.objects.create(supergroup=manager_group, subgroup=leadership_group)

        everyone_group = check_add_user_group(realm, "Everyone", [shiva], acting_user=shiva)
        GroupGroupMembership.objects.create(supergroup=everyone_group, subgroup=staff_group)
        GroupGroupMembership.objects.create(supergroup=everyone_group, subgroup=manager_group)

        subgroup_for_anonymous_supergroup = check_add_user_group(
            realm, "subgroup_for_anonymous_supergroup", [iago], acting_user=iago
        )
        anonymous_supergroup = check_add_user_group(
            realm, "anonymous_supergroup", [iago], acting_user=iago
        )
        GroupGroupMembership.objects.create(
            supergroup=anonymous_supergroup, subgroup=subgroup_for_anonymous_supergroup
        )

        self.assertCountEqual(
            list(get_recursive_subgroups(leadership_group.id)), [leadership_group.usergroup_ptr]
        )
        self.assertCountEqual(
            list(get_recursive_subgroups(staff_group.id)),
            [leadership_group.usergroup_ptr, staff_group.usergroup_ptr],
        )
        self.assertCountEqual(
            list(get_recursive_subgroups(everyone_group.id)),
            [
                leadership_group.usergroup_ptr,
                staff_group.usergroup_ptr,
                everyone_group.usergroup_ptr,
                manager_group.usergroup_ptr,
            ],
        )

        self.assertCountEqual(
            list(get_recursive_subgroups_union_for_groups([staff_group.id, manager_group.id])),
            [
                leadership_group.usergroup_ptr,
                staff_group.usergroup_ptr,
                manager_group.usergroup_ptr,
            ],
        )

        with self.assert_database_query_count(1):
            recursive_supergroups_union_for_groups = list(
                get_recursive_supergroups_union_for_groups(
                    [leadership_group.id, subgroup_for_anonymous_supergroup.id]
                )
            )
        self.assertCountEqual(
            recursive_supergroups_union_for_groups,
            [
                leadership_group.usergroup_ptr,
                everyone_group.usergroup_ptr,
                staff_group.usergroup_ptr,
                manager_group.usergroup_ptr,
                subgroup_for_anonymous_supergroup.usergroup_ptr,
                anonymous_supergroup.usergroup_ptr,
            ],
        )

        self.assertCountEqual(list(get_recursive_strict_subgroups(leadership_group)), [])
        self.assertCountEqual(list(get_recursive_strict_subgroups(staff_group)), [leadership_group])
        self.assertCountEqual(
            list(get_recursive_strict_subgroups(everyone_group)),
            [leadership_group, staff_group, manager_group],
        )

        self.assertCountEqual(list(get_recursive_group_members(leadership_group.id)), [desdemona])
        self.assertCountEqual(list(get_recursive_group_members(staff_group.id)), [desdemona, iago])
        self.assertCountEqual(
            list(get_recursive_group_members(everyone_group.id)),
            [desdemona, iago, shiva, aaron, prospero],
        )

        self.assertCountEqual(
            list(get_recursive_group_members_union_for_groups([staff_group.id, manager_group.id])),
            [iago, desdemona, aaron, prospero],
        )
        self.assertCountEqual(
            list(
                get_recursive_group_members_union_for_groups([leadership_group.id, staff_group.id])
            ),
            [desdemona, iago],
        )

        self.assertIn(leadership_group.usergroup_ptr, get_recursive_membership_groups(desdemona))
        self.assertIn(staff_group.usergroup_ptr, get_recursive_membership_groups(desdemona))
        self.assertIn(everyone_group.usergroup_ptr, get_recursive_membership_groups(desdemona))
        self.assertIn(manager_group.usergroup_ptr, get_recursive_membership_groups(desdemona))

        self.assertIn(staff_group.usergroup_ptr, get_recursive_membership_groups(iago))
        self.assertIn(everyone_group.usergroup_ptr, get_recursive_membership_groups(iago))
        self.assertNotIn(manager_group.usergroup_ptr, get_recursive_membership_groups(iago))

        self.assertIn(everyone_group.usergroup_ptr, get_recursive_membership_groups(shiva))

        do_deactivate_user(iago, acting_user=None)
        self.assertCountEqual(list(get_recursive_group_members(staff_group.id)), [desdemona])
        self.assertCountEqual(
            list(get_recursive_group_members(everyone_group.id)),
            [desdemona, shiva, aaron, prospero],
        )

    def test_get_user_group_member_ids_distinct(self) -> None:
        """
        Verify that when fetching recursive member ids of groups
        we only return distinct ids - without repetitions even if
        there's an overlap between the subgroups/supergroups.
        """
        realm = get_realm("zulip")
        hamlet = self.example_user("hamlet")
        desdemona = self.example_user("desdemona")
        cordelia = self.example_user("cordelia")

        supergroup = check_add_user_group(realm, "S", [hamlet], acting_user=desdemona)

        subgroup1 = check_add_user_group(realm, "A", [cordelia], acting_user=desdemona)
        GroupGroupMembership.objects.create(supergroup=supergroup, subgroup=subgroup1)

        subgroup2 = check_add_user_group(realm, "B", [hamlet], acting_user=desdemona)
        GroupGroupMembership.objects.create(supergroup=supergroup, subgroup=subgroup2)

        # hamlet has both a direct membership in the supergroup, as well as a recursive membership
        # via subgroup2. We make sure that despite that, hamlet.id occurs only once in the result.
        self.assertEqual(
            sorted(get_user_group_member_ids(supergroup)), sorted([hamlet.id, cordelia.id])
        )

    def test_subgroups_of_role_based_system_groups(self) -> None:
        realm = get_realm("zulip")
        owners_group = NamedUserGroup.objects.get(
            realm_for_sharding=realm, name=SystemGroups.OWNERS, is_system_group=True
        )
        admins_group = NamedUserGroup.objects.get(
            realm_for_sharding=realm, name=SystemGroups.ADMINISTRATORS, is_system_group=True
        )
        moderators_group = NamedUserGroup.objects.get(
            realm_for_sharding=realm, name=SystemGroups.MODERATORS, is_system_group=True
        )
        full_members_group = NamedUserGroup.objects.get(
            realm_for_sharding=realm, name=SystemGroups.FULL_MEMBERS, is_system_group=True
        )
        members_group = NamedUserGroup.objects.get(
            realm_for_sharding=realm, name=SystemGroups.MEMBERS, is_system_group=True
        )
        everyone_group = NamedUserGroup.objects.get(
            realm_for_sharding=realm, name=SystemGroups.EVERYONE, is_system_group=True
        )
        everyone_on_internet_group = NamedUserGroup.objects.get(
            realm_for_sharding=realm,
            name=SystemGroups.EVERYONE_ON_INTERNET,
            is_system_group=True,
        )

        self.assertCountEqual(list(get_recursive_strict_subgroups(owners_group)), [])
        self.assertCountEqual(list(get_recursive_strict_subgroups(admins_group)), [owners_group])
        self.assertCountEqual(
            list(get_recursive_strict_subgroups(moderators_group)),
            [owners_group, admins_group],
        )
        self.assertCountEqual(
            list(get_recursive_strict_subgroups(full_members_group)),
            [owners_group, admins_group, moderators_group],
        )
        self.assertCountEqual(
            list(get_recursive_strict_subgroups(members_group)),
            [owners_group, admins_group, moderators_group, full_members_group],
        )
        self.assertCountEqual(
            list(get_recursive_strict_subgroups(everyone_group)),
            [
                owners_group,
                admins_group,
                moderators_group,
                full_members_group,
                members_group,
            ],
        )
        self.assertCountEqual(
            list(get_recursive_strict_subgroups(everyone_on_internet_group)),
            [
                owners_group,
                admins_group,
                moderators_group,
                full_members_group,
                members_group,
                everyone_group,
            ],
        )

    def test_is_user_in_group(self) -> None:
        realm = get_realm("zulip")
        shiva = self.example_user("shiva")
        iago = self.example_user("iago")
        hamlet = self.example_user("hamlet")

        moderators_group = NamedUserGroup.objects.get(
            name=SystemGroups.MODERATORS, realm_for_sharding=realm, is_system_group=True
        )
        administrators_group = NamedUserGroup.objects.get(
            name=SystemGroups.ADMINISTRATORS, realm_for_sharding=realm, is_system_group=True
        )

        self.assertTrue(is_user_in_group(moderators_group.id, shiva))

        # Iago is member of a subgroup of moderators group.
        self.assertTrue(is_user_in_group(moderators_group.id, iago))
        self.assertFalse(is_user_in_group(moderators_group.id, iago, direct_member_only=True))
        self.assertTrue(is_user_in_group(administrators_group.id, iago, direct_member_only=True))

        self.assertFalse(is_user_in_group(moderators_group.id, hamlet))
        self.assertFalse(is_user_in_group(moderators_group.id, hamlet, direct_member_only=True))

        do_deactivate_user(iago, acting_user=None)
        self.assertFalse(is_user_in_group(moderators_group.id, iago))
        self.assertFalse(is_user_in_group(administrators_group.id, iago, direct_member_only=True))

    def test_is_any_user_in_group(self) -> None:
        realm = get_realm("zulip")
        shiva = self.example_user("shiva").id
        iago = self.example_user("iago").id
        hamlet = self.example_user("hamlet").id
        polonius = self.example_user("polonius").id

        moderators_group = NamedUserGroup.objects.get(
            name=SystemGroups.MODERATORS, realm_for_sharding=realm, is_system_group=True
        )
        administrators_group = NamedUserGroup.objects.get(
            name=SystemGroups.ADMINISTRATORS, realm_for_sharding=realm, is_system_group=True
        )

        self.assertTrue(is_any_user_in_group(moderators_group.id, [shiva, hamlet, polonius]))

        # Iago is member of a subgroup of moderators group.
        self.assertTrue(is_any_user_in_group(moderators_group.id, [iago, hamlet, polonius]))
        self.assertFalse(
            is_any_user_in_group(
                moderators_group.id, [iago, hamlet, polonius], direct_member_only=True
            )
        )
        self.assertTrue(
            is_any_user_in_group(
                administrators_group.id, [iago, shiva, hamlet], direct_member_only=True
            )
        )

        self.assertFalse(is_any_user_in_group(moderators_group.id, [hamlet, polonius]))
        self.assertFalse(
            is_any_user_in_group(moderators_group.id, [hamlet], direct_member_only=True)
        )

    def test_has_user_group_access_for_subgroup(self) -> None:
        iago = self.example_user("iago")
        zulip_realm = get_realm("zulip")
        zulip_group = check_add_user_group(zulip_realm, "zulip", [], acting_user=iago)
        moderators_group = NamedUserGroup.objects.get(
            name=SystemGroups.MODERATORS, realm_for_sharding=zulip_realm, is_system_group=True
        )

        lear_realm = get_realm("lear")
        lear_group = check_add_user_group(lear_realm, "test", [], acting_user=iago)

        self.assertFalse(has_user_group_access_for_subgroup(lear_group, iago))
        self.assertTrue(has_user_group_access_for_subgroup(zulip_group, iago))
        self.assertTrue(has_user_group_access_for_subgroup(moderators_group, iago))

    def test_get_system_user_group_by_name(self) -> None:
        realm = get_realm("zulip")
        moderators_group = NamedUserGroup.objects.get(
            name=SystemGroups.MODERATORS, realm_for_sharding=realm, is_system_group=True
        )

        self.assertEqual(
            get_system_user_group_by_name(SystemGroups.MODERATORS, realm.id), moderators_group
        )

        with self.assertRaisesRegex(JsonableError, "Invalid system group name."):
            get_system_user_group_by_name("hamletcharacters", realm.id)

    def test_update_user_group_members_noop_case(self) -> None:
        hamlet = self.example_user("hamlet")
        test_group = check_add_user_group(
            hamlet.realm,
            "test_group",
            [self.example_user("othello")],
            "Test group",
            acting_user=self.example_user("othello"),
        )
        # These functions should not do anything if any of the list
        # arguments is empty.
        with self.capture_send_event_calls(expected_num_events=0):
            bulk_add_members_to_user_groups([], [hamlet.id], acting_user=None)
            bulk_add_members_to_user_groups([test_group], [], acting_user=None)
            bulk_remove_members_from_user_groups([], [hamlet.id], acting_user=None)
            bulk_remove_members_from_user_groups([test_group], [], acting_user=None)
            add_subgroups_to_user_group(test_group, [], acting_user=None)
            remove_subgroups_from_user_group(test_group, [], acting_user=None)

    def test_check_user_has_permission_by_role(self) -> None:
        realm = get_realm("zulip")
        system_groups_name_dict = get_realm_system_groups_name_dict(realm.id)

        desdemona = self.example_user("desdemona")
        iago = self.example_user("iago")
        shiva = self.example_user("shiva")
        hamlet = self.example_user("hamlet")
        othello = self.example_user("othello")
        polonius = self.example_user("polonius")

        nobody_group = NamedUserGroup.objects.get(
            name=SystemGroups.NOBODY, realm_for_sharding=realm, is_system_group=True
        )
        self.assertFalse(
            check_user_has_permission_by_role(desdemona, nobody_group.id, system_groups_name_dict)
        )

        owners_group = NamedUserGroup.objects.get(
            name=SystemGroups.OWNERS, realm_for_sharding=realm, is_system_group=True
        )
        self.assertFalse(
            check_user_has_permission_by_role(iago, owners_group.id, system_groups_name_dict)
        )
        self.assertTrue(
            check_user_has_permission_by_role(desdemona, owners_group.id, system_groups_name_dict)
        )

        admins_group = NamedUserGroup.objects.get(
            name=SystemGroups.ADMINISTRATORS, realm_for_sharding=realm, is_system_group=True
        )
        self.assertFalse(
            check_user_has_permission_by_role(shiva, admins_group.id, system_groups_name_dict)
        )
        self.assertTrue(
            check_user_has_permission_by_role(iago, admins_group.id, system_groups_name_dict)
        )
        self.assertTrue(
            check_user_has_permission_by_role(desdemona, admins_group.id, system_groups_name_dict)
        )

        moderators_group = NamedUserGroup.objects.get(
            name=SystemGroups.MODERATORS, realm_for_sharding=realm, is_system_group=True
        )
        self.assertFalse(
            check_user_has_permission_by_role(hamlet, moderators_group.id, system_groups_name_dict)
        )
        self.assertTrue(
            check_user_has_permission_by_role(shiva, moderators_group.id, system_groups_name_dict)
        )
        self.assertTrue(
            check_user_has_permission_by_role(iago, moderators_group.id, system_groups_name_dict)
        )

        members_group = NamedUserGroup.objects.get(
            name=SystemGroups.MEMBERS, realm_for_sharding=realm, is_system_group=True
        )
        self.assertFalse(
            check_user_has_permission_by_role(polonius, members_group.id, system_groups_name_dict)
        )
        self.assertTrue(
            check_user_has_permission_by_role(hamlet, members_group.id, system_groups_name_dict)
        )

        full_members_group = NamedUserGroup.objects.get(
            name=SystemGroups.FULL_MEMBERS, realm_for_sharding=realm, is_system_group=True
        )
        do_set_realm_property(realm, "waiting_period_threshold", 10, acting_user=None)
        hamlet.refresh_from_db()
        shiva.refresh_from_db()
        othello.refresh_from_db()
        polonius.refresh_from_db()

        hamlet.date_joined = timezone_now() - timedelta(days=9)
        hamlet.save()

        shiva.date_joined = timezone_now() - timedelta(days=9)
        shiva.save()

        othello.date_joined = timezone_now() - timedelta(days=11)
        othello.save()

        polonius.date_joined = timezone_now() - timedelta(days=11)
        polonius.save()

        self.assertFalse(
            check_user_has_permission_by_role(
                polonius, full_members_group.id, system_groups_name_dict
            )
        )
        self.assertFalse(
            check_user_has_permission_by_role(
                hamlet, full_members_group.id, system_groups_name_dict
            )
        )
        self.assertTrue(
            check_user_has_permission_by_role(
                othello, full_members_group.id, system_groups_name_dict
            )
        )
        self.assertTrue(
            check_user_has_permission_by_role(shiva, full_members_group.id, system_groups_name_dict)
        )

        everyone_group = NamedUserGroup.objects.get(
            name=SystemGroups.EVERYONE, realm_for_sharding=realm, is_system_group=True
        )
        self.assertTrue(
            check_user_has_permission_by_role(polonius, everyone_group.id, system_groups_name_dict)
        )

    def test_user_group_ids_to_user_groups(self) -> None:
        realm = get_realm("zulip")
        hamletcharacters_group = NamedUserGroup.objects.get(
            name="hamletcharacters", realm_for_sharding=realm
        )
        admins_group = NamedUserGroup.objects.get(
            name=SystemGroups.ADMINISTRATORS, realm_for_sharding=realm, is_system_group=True
        )
        members_group = NamedUserGroup.objects.get(
            name=SystemGroups.MEMBERS, realm_for_sharding=realm, is_system_group=True
        )

        self.assertCountEqual(
            user_group_ids_to_user_groups(
                [hamletcharacters_group.id, admins_group.id, members_group.id], realm
            ),
            [hamletcharacters_group, admins_group, members_group],
        )
        self.assertCountEqual(
