# Generated by Django 3.2.6 on 2021-12-14 09:16
import math
import secrets

from django.db import migrations, models

from tqdm import tqdm


def update_batch_size():
    # Exists as a function purely so tests can mock the return_value
    return 1000


def reverse(apps, schema_editor):
    FormView = apps.get_model("database", "FormView")

    print("Migrating view public and slug properties back to form view...")
    for f in FormView.objects.all():
        view = f.view_ptr
        f.public = view.public_temp
        f.slug = view.slug_temp
        f.save()


# noinspection PyPep8Naming
def forward(apps, schema_editor):
    FormView = apps.get_model("database", "FormView")
    View = apps.get_model("database", "View")

    _copy_slug_and_public_from_form_to_view(FormView, View)
    _generate_slugs_for_views(View)


def _copy_slug_and_public_from_form_to_view(FormView, View):
    print("Migrating form view public and slug properties to View...")
    updated_form_views = []
    for f in FormView.objects.all():
        view = f.view_ptr
        view.public_temp = f.public
        view.slug_temp = f.slug
        updated_form_views.append(view)
    View.objects.bulk_update(updated_form_views, fields=["public_temp", "slug_temp"])
    print("Done with form view")


def _generate_slugs_for_views(View):
    views_to_generate_slugs_for = View.objects.filter(slug_temp__isnull=True)
    view_count = views_to_generate_slugs_for.count()
    batch_size = update_batch_size()
    updated_views = []

    # Use tqdm in manual mode as it doesn't work nicely wrapping generators like
    # .iterator()
    with tqdm(
        total=math.ceil(view_count / batch_size),
        desc=f"Generating slugs for {view_count} views in batches of {batch_size}",
    ) as pbar:
        for view in views_to_generate_slugs_for.iterator():
            view.slug_temp = secrets.token_urlsafe()
            updated_views.append(view)
            if len(updated_views) >= batch_size:
                View.objects.bulk_update(updated_views, fields=["slug_temp"])
                updated_views.clear()
                pbar.update(1)
        View.objects.bulk_update(updated_views, fields=["slug_temp"])
        pbar.update(1)


class Migration(migrations.Migration):
    dependencies = [
        ("database", "0052_table_order_and_id_index"),
    ]

    operations = [
        migrations.RunSQL(
            "SET CONSTRAINTS ALL IMMEDIATE", reverse_sql=migrations.RunSQL.noop
        ),
        migrations.AddField(
            model_name="view",
            name="public_temp",
            field=models.BooleanField(
                default=False,
                help_text="Indicates whether the view is publicly accessible to "
                "visitors.",
            ),
        ),
        migrations.AddField(
            model_name="view",
            name="slug_temp",
            field=models.SlugField(
                null=True,
                blank=True,
                help_text="The unique slug where the view can be accessed publicly on.",
                unique=True,
                db_index=True,
            ),
        ),
        # Ensure that the slug is nullable on the formview so when migrating backwards
        # so that the field is created filled with nulls which we then copy
        # view.slug into. The reverse of this AlterField will set null=False,
        # blank=False, default=secrets.token_urlsafe.
        migrations.AlterField(
            model_name="formview",
            name="slug",
            field=models.SlugField(
                help_text="The unique slug where the form can be accessed "
                "publicly on.",
                null=True,
                blank=True,
                unique=True,
                db_index=True,
            ),
        ),
        migrations.RunPython(forward, reverse),
        migrations.AlterField(
            model_name="view",
            name="slug_temp",
            field=models.SlugField(
                help_text="The unique slug where the view can be accessed publicly on.",
                default=secrets.token_urlsafe,
                unique=True,
                db_index=True,
            ),
        ),
        migrations.RemoveField(
            model_name="formview",
            name="public",
        ),
        migrations.RemoveField(
            model_name="formview",
            name="slug",
        ),
        migrations.RenameField(
            model_name="view",
            old_name="public_temp",
            new_name="public",
        ),
        migrations.RenameField(
            model_name="view",
            old_name="slug_temp",
            new_name="slug",
        ),
        migrations.RunSQL(
            migrations.RunSQL.noop, reverse_sql="SET CONSTRAINTS ALL IMMEDIATE"
        ),
    ]
