# Generated by Django 4.2.24
"""Backfill slugs for corpuses and users that lack them.

User slug backfilling lives in this corpuses migration (rather than a users
migration) because the root cause is corpuses migration 0038
(create_personal_corpuses), which created personal corpuses via historical
models that bypass Corpus.save() and its slug auto-generation.  Keeping
the fix next to the cause avoids cross-app dependency confusion.
"""

import logging
import re

from django.db import migrations
from django.db.models import Q

logger = logging.getLogger(__name__)

ALLOWED_SLUG_PATTERN = re.compile(r"[^A-Za-z0-9-]+")


def _sanitize_slug(value, max_length=128):
    """Inline slug sanitizer (migrations cannot import model utilities)."""
    if value is None:
        value = ""
    value = value.replace(" ", "-").replace("_", "-")
    value = ALLOWED_SLUG_PATTERN.sub("", value)
    value = re.sub(r"-+", "-", value)
    value = value.strip("-")
    if max_length and len(value) > max_length:
        value = value[:max_length]
    return value


def _generate_unique_slug(
    base_value, scope_qs, max_length=128, fallback_prefix="corpus"
):
    """Inline unique slug generator for migrations."""
    base_slug = _sanitize_slug(base_value or "", max_length)
    if not base_slug:
        base_slug = fallback_prefix

    candidate = base_slug
    suffix = 2
    while scope_qs.filter(slug=candidate).exists():
        trimmed = base_slug[: max(1, max_length - (len(str(suffix)) + 1))]
        candidate = f"{trimmed}-{suffix}"
        suffix += 1

    return candidate


def backfill_corpus_slugs(apps, schema_editor):
    """Backfill slugs for any Corpus records that have a NULL or empty slug.

    Also backfills User slugs, since corpus navigation requires both
    corpus.slug and creator.slug to build the URL /c/<user>/<corpus>.
    """
    User = apps.get_model("users", "User")
    Corpus = apps.get_model("corpuses", "Corpus")

    # --- User slugs ---
    users_without_slugs = User.objects.filter(Q(slug__isnull=True) | Q(slug=""))
    user_count = 0
    for user in users_without_slugs:
        scope_qs = User.objects.exclude(pk=user.pk)
        base_value = user.username or user.email or "user"
        user.slug = _generate_unique_slug(
            base_value, scope_qs, max_length=64, fallback_prefix="user"
        )
        user.save(update_fields=["slug"])
        user_count += 1
        logger.info(f"Backfilled slug '{user.slug}' for user {user.pk}")

    logger.info(f"Backfilled slugs for {user_count} users")

    # --- Corpus slugs ---
    corpuses_without_slugs = Corpus.objects.filter(
        Q(slug__isnull=True) | Q(slug="")
    )
    corpus_count = 0

    for corpus in corpuses_without_slugs.select_related("creator"):
        scope_qs = Corpus.objects.filter(creator_id=corpus.creator_id).exclude(
            pk=corpus.pk
        )
        corpus.slug = _generate_unique_slug(corpus.title, scope_qs)
        corpus.save(update_fields=["slug"])
        corpus_count += 1
        logger.info(
            f"Backfilled slug '{corpus.slug}' for corpus {corpus.pk} "
            f"(creator={corpus.creator_id})"
        )

    logger.info(f"Backfilled slugs for {corpus_count} corpuses")


class Migration(migrations.Migration):
    """Data migration to backfill slugs for corpuses and users missing them.

    Migration 0038 created personal corpuses using historical models which
    bypass Corpus.save() slug auto-generation. Users created before the slug
    field was added (migration users/0015) may also lack slugs. Navigation
    requires both corpus.slug and creator.slug to build /c/<user>/<corpus>
    URLs. This migration ensures all records have valid slugs.
    """

    dependencies = [
        ("corpuses", "0042_update_checkconstraint_check_to_condition"),
        ("users", "0025_alter_userexport_format_add_v2"),
    ]

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