# Generated by Django 5.1.12 on 2025-09-30 12:29

from django.db import migrations, models

from django.apps.registry import Apps
from django.db.backends.base.schema import BaseDatabaseSchemaEditor


def migrate_object_permissions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
    db_alias = schema_editor.connection.alias

    User = apps.get_model("authentik_core", "User")
    Group = apps.get_model("auth", "Group")
    Role = apps.get_model("authentik_rbac", "Role")
    UserObjectPermission = apps.get_model("guardian", "UserObjectPermission")
    GroupObjectPermission = apps.get_model("guardian", "GroupObjectPermission")
    RoleObjectPermission = apps.get_model("guardian", "RoleObjectPermission")
    RoleModelPermission = apps.get_model("guardian", "RoleModelPermission")

    def get_role_for_user_id(user_id: int) -> Role:
        name = f"ak-migrated-role--user-{user_id}"
        role, created = Role.objects.using(db_alias).get_or_create(
            name=name,
        )
        if created:
            role.users.add(user_id)
        return role

    def get_role_for_group_id(group_id: int) -> Role:
        role = Role.objects.using(db_alias).filter(group_id=group_id).first()
        if not role:
            # Every django group should already have a role, so this should never happen.
            # But let's be nice.
            name = f"ak-migrated-role--group-{group_id}"
            role, created = Role.objects.using(db_alias).get_or_create(
                group_id=group_id,
                name=name,
            )
            if created:
                role.group_id = group_id
                role.save()
        return role

    # Below are 4 very similar pieces of code, for (user, group) x (model, object).
    # Since this is a one-off migration, I won't attempt DRYing them.

    # User model permissions
    user_ids_with_model_permissions = (
        User.user_permissions.through.objects.using(db_alias)
        .values_list("user", flat=True)
        .distinct()
    )
    for user_id in user_ids_with_model_permissions:
        role = get_role_for_user_id(user_id)
        user_model_permissions = User.user_permissions.through.objects.using(db_alias).filter(
            user_id=user_id
        )

        role_model_permissions = []
        for user_model_permission in user_model_permissions:
            role_model_permissions.append(
                RoleModelPermission(
                    permission=user_model_permission.permission,
                    content_type=user_model_permission.permission.content_type,
                    role=role,
                )
            )

        RoleModelPermission.objects.using(db_alias).bulk_create(role_model_permissions)

    # Group model permissions
    group_ids_with_model_permissions = (
        Group.permissions.through.objects.using(db_alias).values_list("group", flat=True).distinct()
    )
    for group_id in group_ids_with_model_permissions:
        role = get_role_for_group_id(group_id)
        group_model_permissions = Group.permissions.through.objects.using(db_alias).filter(
            group_id=group_id
        )

        role_model_permissions = []
        for group_model_permission in group_model_permissions:
            role_model_permissions.append(
                RoleModelPermission(
                    permission=group_model_permission.permission,
                    content_type=group_model_permission.permission.content_type,
                    role=role,
                )
            )

        RoleModelPermission.objects.using(db_alias).bulk_create(role_model_permissions)

    # User object permissions
    user_ids_with_object_permissions = (
        UserObjectPermission.objects.using(db_alias).values_list("user", flat=True).distinct()
    )
    for user_id in user_ids_with_object_permissions:
        role = get_role_for_user_id(user_id)
        user_object_permissions = UserObjectPermission.objects.using(db_alias).filter(user=user_id)

        role_object_permissions = []
        for user_object_permission in user_object_permissions:
            role_object_permissions.append(
                RoleObjectPermission(
                    permission=user_object_permission.permission,
                    content_type=user_object_permission.content_type,
                    object_pk=user_object_permission.object_pk,
                    role=role,
                )
            )

        RoleObjectPermission.objects.using(db_alias).bulk_create(role_object_permissions)

    # Group object permissions
    group_ids_with_object_permissions = (
        GroupObjectPermission.objects.using(db_alias).values_list("group", flat=True).distinct()
    )
    for group_id in group_ids_with_object_permissions:
        role = get_role_for_group_id(group_id)
        group_object_permissions = GroupObjectPermission.objects.using(db_alias).filter(
            group=group_id
        )

        role_object_permissions = []
        for group_object_permission in group_object_permissions:
            role_object_permissions.append(
                RoleObjectPermission(
                    permission=group_object_permission.permission,
                    content_type=group_object_permission.content_type,
                    object_pk=group_object_permission.object_pk,
                    role=role,
                )
            )

        RoleObjectPermission.objects.using(db_alias).bulk_create(role_object_permissions)


class Migration(migrations.Migration):

    dependencies = [
        ("guardian", "0004_role_permissions"),
        ("authentik_core", "0055_groupancestor_groupparentagenode_group_parents"),
        ("authentik_rbac", "0008_alter_role_group"),
    ]

    operations = [
        migrations.AddField(
            model_name="user",
            name="roles",
            field=models.ManyToManyField(
                blank=True, related_name="users", to="authentik_rbac.role"
            ),
        ),
        migrations.RunPython(migrate_object_permissions),
        migrations.AlterUniqueTogether(
            name="group",
            unique_together=set(),
        ),
        migrations.AlterField(
            model_name="group",
            name="parents",
            field=models.ManyToManyField(
                blank=True,
                related_name="children",
                through="authentik_core.GroupParentageNode",
                to="authentik_core.group",
            ),
        ),
        migrations.RemoveField(
            model_name="group",
            name="parent",
        ),
        migrations.AlterField(
            model_name="group",
            name="name",
            field=models.TextField(unique=True, verbose_name="name"),
        ),
    ]
