from django.db import migrations, transaction


def generate_unique_sku(make_candidate, taken):
    while True:
        c = make_candidate()
        if c not in taken:
            taken.add(c)
            return c


# noinspection PyUnusedLocal
def backfill_sku(apps, schema_editor):
    Product = apps.get_model("core", "Product")
    from core.utils import generate_human_readable_id as make_candidate

    taken = set(Product.objects.exclude(sku__isnull=True).values_list("sku", flat=True))

    BATCH = 10000
    last_pk = 0

    while True:
        ids = list(
            Product.objects.filter(sku__isnull=True, pk__gt=last_pk).order_by("pk").values_list("pk", flat=True)[:BATCH]
        )
        if not ids:
            break

        updates = []
        for pk in ids:
            updates.append(Product(pk=pk, sku=generate_unique_sku(make_candidate, taken)))

        with transaction.atomic():
            Product.objects.bulk_update(updates, ["sku"], batch_size=BATCH)

        last_pk = ids[-1]


# noinspection PyUnusedLocal
def noop(apps, schema_editor):
    pass


class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ("core", "0037_product_sku"),
    ]

    operations = [
        migrations.RunPython(backfill_sku, reverse_code=noop),
    ]
