# Generated by Django 5.2.3 on 2025-07-04 20:33

from datetime import timedelta

from django.conf import settings
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 Max


def migrate_realmcreationkey_to_realmcreationstatus(
    apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
    """
    This migration is for switching from using a separate RealmCreationKey class
    for realm creation keys to just using the Confirmation system.

    The aim is to iterate through all the existing RealmCreationKey and create
    a corresponding Confirmation+RealmCreationStatus.

    For validity of these objects, we only need to worry about RealmCreationKeys
    expired due to time. This is taken care of by making sure we set expiry_date
    on the Confirmation we're creating.
    The way the RealmCreationKey system worked was to .delete() the RealmCreationKey
    objects when they were used - so those simply no longer exist and we don't need to
    worry about this case.
    """
    CAN_CREATE_REALM = 11

    Confirmation = apps.get_model("confirmation", "Confirmation")
    ContentType = apps.get_model("contenttypes", "ContentType")
    RealmCreationKey = apps.get_model("confirmation", "RealmCreationKey")
    RealmCreationStatus = apps.get_model("zerver", "RealmCreationStatus")
    realm_creation_status_content_type, _created = ContentType.objects.get_or_create(
        model="realmcreationstatus", app_label="zerver"
    )

    BATCH_SIZE = 10000
    max_id = RealmCreationKey.objects.aggregate(Max("id"))["id__max"]
    if max_id is None:
        # Nothing to do.
        return

    lower_bound = 1
    while lower_bound <= max_id + (BATCH_SIZE / 2):
        upper_bound = lower_bound + BATCH_SIZE - 1
        creation_keys = RealmCreationKey.objects.filter(id__range=(lower_bound, upper_bound))
        creation_statuses_to_create = []
        confirmations_to_create = []
        for creation_key in creation_keys:
            # These keys were generated in the same way as keys for Confirmation objects,
            # so we can copy them over without breaking anything.
            key = creation_key.creation_key
            date_created = creation_key.date_created
            presume_email_valid = creation_key.presume_email_valid

            creation_status = RealmCreationStatus(
                status=0, date_created=date_created, presume_email_valid=presume_email_valid
            )
            confirmation = Confirmation(
                content_type=realm_creation_status_content_type,
                type=CAN_CREATE_REALM,
                confirmation_key=key,
                date_sent=date_created,
                expiry_date=date_created
                + timedelta(days=settings.CAN_CREATE_REALM_LINK_VALIDITY_DAYS),
            )

            # To attach the Confirmations to RealmCreationStatus objects we need to set the
            # confirmation.object_id to their respective ids. But we haven't saved
            # the RealmCreationStatus objs to the database yet - so we don't have ids.
            #
            # After we .bulk_create() them, their .id attributes will be populated.
            # So for now we just link the RealmCreationStatus to the Confirmation
            # via a temporary ._object attribute - which we'll use later to set
            # the .object_id as intended.
            confirmation._object = creation_status

            creation_statuses_to_create.append(creation_status)
            confirmations_to_create.append(confirmation)
        with transaction.atomic():
            RealmCreationStatus.objects.bulk_create(creation_statuses_to_create)

            # Now the objects in creation_statuses_to_create have had their .id
            # attrs populated. For every confirmation, confirmation._object
            # points to its corresponding RealmCreationStatus - so now we can
            # set the confirmation.object_id values and clear out the temporary
            # ._object attr.
            for confirmation in confirmations_to_create:
                confirmation.object_id = confirmation._object.id
                delattr(confirmation, "_object")
            Confirmation.objects.bulk_create(confirmations_to_create)

        lower_bound += BATCH_SIZE


class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ("confirmation", "0015_alter_confirmation_object_id"),
        ("zerver", "0747_realmcreationstatus"),
    ]

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