# Generated by Django 5.2.6 on 2025-09-22 15:11

from django.db import migrations, transaction
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from django.db.migrations.state import StateApps
from django.db.models import Value


def handle_same_anonymous_group_used_for_multiple_streams(
    apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
    Stream = apps.get_model("zerver", "Stream")
    Realm = apps.get_model("zerver", "Realm")
    UserGroup = apps.get_model("zerver", "UserGroup")
    UserGroupMembership = apps.get_model("zerver", "UserGroupMembership")
    GroupGroupMembership = apps.get_model("zerver", "GroupGroupMembership")

    STREAM_GROUP_PERMISSION_SETTINGS_TO_UPDATE = [
        # IMPORTANT: This is a list of the groups that existed at the
        # time of the migration, and should never be updated.
        "can_add_subscribers_group",
        "can_administer_channel_group",
        "can_delete_any_message_group",
        "can_delete_own_message_group",
        "can_move_messages_out_of_channel_group",
        "can_move_messages_within_channel_group",
        "can_remove_subscribers_group",
        "can_resolve_topics_group",
        "can_send_message_group",
        "can_subscribe_group",
    ]

    def get_members_and_subgroups_of_groups(
        group_ids: list[int],
    ) -> dict[int, dict[str, list[int]]]:
        user_members = (
            UserGroupMembership.objects.filter(user_group_id__in=group_ids)
            .annotate(
                member_type=Value("user"),
            )
            .values_list("member_type", "user_group_id", "user_profile_id")
        )

        group_subgroups = (
            GroupGroupMembership.objects.filter(supergroup_id__in=group_ids)
            .annotate(
                member_type=Value("group"),
            )
            .values_list("member_type", "supergroup_id", "subgroup_id")
        )

        results_dict: dict[int, dict[str, list[int]]] = dict()
        for group_id in group_ids:
            results_dict[group_id] = dict(direct_members=[], direct_subgroups=[])

        all_members = user_members.union(group_subgroups)
        for member_type, group_id, member_id in all_members:
            members_dict = results_dict[group_id]
            if member_type == "user":
                members_dict["direct_members"].append(member_id)
            else:
                members_dict["direct_subgroups"].append(member_id)

        return results_dict

    for realm in Realm.objects.all().iterator():
        anonymous_group_ids = UserGroup.objects.filter(
            realm=realm, named_user_group=None
        ).values_list("id", flat=True)
        members_dict = get_members_and_subgroups_of_groups(anonymous_group_ids)

        used_anonymous_group_ids = set()
        user_groups_to_create = []
        streams_to_update = []
        updated_fields = set()

        user_group_memberships_to_create = []
        subgroup_memberships_to_create = []

        for stream in Stream.objects.filter(realm=realm):
            stream_updated = False
            for setting_name in STREAM_GROUP_PERMISSION_SETTINGS_TO_UPDATE:
                setting_group_id = getattr(stream, setting_name + "_id")
                if setting_group_id in anonymous_group_ids:
                    if setting_group_id not in used_anonymous_group_ids:
                        used_anonymous_group_ids.add(setting_group_id)
                        continue

                    # We have an anonymous group that was used twice;
                    # correct this database corruption by creating a
                    # new anonymous group duplicating its contents.
                    print(
                        f"Fixing duplicate anonymous group for channel {stream.id} / {setting_name}"
                    )
                    user_group = UserGroup(realm=realm)
                    setattr(stream, setting_name, user_group)

                    user_groups_to_create.append(user_group)
                    user_group_memberships_to_create.extend(
                        [
                            UserGroupMembership(user_group=user_group, user_profile_id=user_id)
                            for user_id in members_dict[setting_group_id]["direct_members"]
                        ]
                    )
                    subgroup_memberships_to_create.extend(
                        [
                            GroupGroupMembership(supergroup=user_group, subgroup_id=subgroup_id)
                            for subgroup_id in members_dict[setting_group_id]["direct_subgroups"]
                        ]
                    )

                    updated_fields.add(setting_name)
                    stream_updated = True

            if stream_updated:
                streams_to_update.append(stream)

        if streams_to_update:
            with transaction.atomic():
                UserGroup.objects.bulk_create(user_groups_to_create)
                Stream.objects.bulk_update(streams_to_update, fields=updated_fields)
                UserGroupMembership.objects.bulk_create(user_group_memberships_to_create)
                GroupGroupMembership.objects.bulk_create(subgroup_memberships_to_create)


class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        (
            "zerver",
            "0755_usermessage_zerver_usermessage_message_active_mobile_push_notification_idx",
        ),
    ]

    operations = [
        migrations.RunPython(
            handle_same_anonymous_group_used_for_multiple_streams,
            reverse_code=migrations.RunPython.noop,
            elidable=True,
        )
    ]
