# Generated by Django 4.2.2 on 2023-07-03 06:39

from typing import Any

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

INVALID_NAME_PREFIXES = ["@", "role:", "user:", "stream:", "channel:", "group:"]


def check_group_name_starts_with_invalid_prefix(group_name: str) -> str:
    for invalid_prefix in INVALID_NAME_PREFIXES:
        if group_name.startswith(invalid_prefix):
            return invalid_prefix
    return ""


def remove_invalid_characters_from_user_group_name(
    apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
    UserGroup = apps.get_model("zerver", "UserGroup")
    Realm = apps.get_model("zerver", "Realm")

    @transaction.atomic
    def update_single_realm(realm: Any) -> None:
        # Remove "@" character from name of role-based system groups.
        groups_to_update = []
        for group in UserGroup.objects.filter(realm=realm, is_system_group=True):
            if group.name[0] != "@":
                # We skip the group if its name has already been updated.
                continue

            old_group_name = group.name
            group.name = old_group_name.removeprefix("@")
            groups_to_update.append(group)

        # Fix the name of non-system groups as well.
        existing_group_names = UserGroup.objects.filter(
            realm=realm, is_system_group=False
        ).values_list("name", flat=True)
        existing_group_names_set = set(existing_group_names)
        for prefix in INVALID_NAME_PREFIXES:
            groups = UserGroup.objects.filter(
                realm=realm, is_system_group=False, name__startswith=prefix
            )
            for group in groups:
                orig_group_name = group.name
                group_name = group.name
                while True:
                    matching_invalid_prefix = check_group_name_starts_with_invalid_prefix(
                        group_name
                    )
                    if len(matching_invalid_prefix) == 0:
                        break
                    group_name = group_name.removeprefix(matching_invalid_prefix)

                if len(group_name) > 0 and group_name not in existing_group_names_set:
                    group.name = group_name
                else:
                    group.name = f"group:{group.id}"

                existing_group_names_set.add(group.name)
                groups_to_update.append(group)
                print(f"Renaming {orig_group_name} to {group_name} for {group.id} in {realm.id}")

        UserGroup.objects.bulk_update(groups_to_update, ["name"])

    for realm in Realm.objects.all().iterator():
        update_single_realm(realm)


class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ("zerver", "0458_realmauditlog_modified_user_group"),
    ]

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