# Generated by Django 4.2.24

import logging

from django.db import migrations

logger = logging.getLogger(__name__)


def create_personal_corpuses_and_move_standalone_docs(apps, schema_editor):
    """
    Create personal corpuses for all existing active users who don't have one,
    then move standalone documents (documents without any DocumentPath) into
    their creator's personal corpus.
    """
    User = apps.get_model("users", "User")
    Corpus = apps.get_model("corpuses", "Corpus")
    Document = apps.get_model("documents", "Document")
    DocumentPath = apps.get_model("documents", "DocumentPath")
    # Get guardian permission models
    CorpusUserObjectPermission = apps.get_model(
        "corpuses", "CorpusUserObjectPermission"
    )
    Permission = apps.get_model("auth", "Permission")
    ContentType = apps.get_model("contenttypes", "ContentType")

    # Get content type for Corpus
    try:
        corpus_ct = ContentType.objects.get(app_label="corpuses", model="corpus")
    except ContentType.DoesNotExist:
        logger.warning("ContentType for Corpus not found, skipping permissions")
        corpus_ct = None

    # Get all required permissions
    permission_codenames = [
        "create_corpus",
        "read_corpus",
        "update_corpus",
        "remove_corpus",
        "publish_corpus",
        "permission_corpus",
        "comment_corpus",
    ]
    permissions = {}
    if corpus_ct:
        for codename in permission_codenames:
            try:
                perm = Permission.objects.get(codename=codename, content_type=corpus_ct)
                permissions[codename] = perm
            except Permission.DoesNotExist:
                logger.warning(f"Permission {codename} not found")

    # Step 1: Create personal corpuses for users who don't have one
    users_without_personal = (
        User.objects.filter(is_active=True).exclude(corpus__is_personal=True).distinct()
    )

    created_count = 0
    for user in users_without_personal:
        # Double-check no personal corpus exists (defensive)
        if Corpus.objects.filter(creator=user, is_personal=True).exists():
            continue

        # Create personal corpus
        corpus = Corpus.objects.create(
            title="My Documents",
            description="Your personal document collection",
            creator=user,
            is_personal=True,
            is_public=False,
        )
        created_count += 1

        # Grant all permissions to user
        if corpus_ct:
            for perm in permissions.values():
                CorpusUserObjectPermission.objects.get_or_create(
                    user=user,
                    content_object=corpus,
                    permission=perm,
                )

        logger.info(f"Created personal corpus {corpus.pk} for user {user.pk}")

    logger.info(f"Created {created_count} personal corpuses for existing users")

    # Step 2: Find standalone documents (documents without any DocumentPath)
    # These are documents that exist but aren't linked to any corpus via DocumentPath
    docs_with_paths = DocumentPath.objects.values_list("document_id", flat=True)
    standalone_docs = Document.objects.exclude(pk__in=docs_with_paths)

    moved_count = 0
    for doc in standalone_docs:
        # Get the document creator's personal corpus
        personal_corpus = Corpus.objects.filter(
            creator=doc.creator,
            is_personal=True,
        ).first()

        if not personal_corpus:
            # This shouldn't happen if Step 1 worked, but handle it
            logger.warning(
                f"No personal corpus found for user {doc.creator_id}, "
                f"creating one for document {doc.pk}"
            )
            personal_corpus = Corpus.objects.create(
                title="My Documents",
                description="Your personal document collection",
                creator=doc.creator,
                is_personal=True,
                is_public=False,
            )
            # Grant permissions
            if corpus_ct:
                for perm in permissions.values():
                    CorpusUserObjectPermission.objects.get_or_create(
                        user=doc.creator,
                        content_object=personal_corpus,
                        permission=perm,
                    )

        # Generate a safe path from the document title
        safe_title = "".join(
            c if c.isalnum() or c in "-_." else "_"
            for c in (doc.title or "document")[:100]
        )
        base_path = f"/documents/{safe_title or f'doc_{doc.pk}'}"

        # Check for path collision and make unique if needed
        path = base_path
        counter = 1
        while DocumentPath.objects.filter(
            corpus=personal_corpus,
            path=path,
            is_current=True,
            is_deleted=False,
        ).exists():
            path = f"{base_path}_{counter}"
            counter += 1

        # Create DocumentPath to link document to personal corpus
        DocumentPath.objects.create(
            document=doc,
            corpus=personal_corpus,
            folder=None,
            path=path,
            version_number=1,
            is_current=True,
            is_deleted=False,
            creator=doc.creator,
        )

        # Also add to M2M for backwards compatibility with legacy queries.
        # NOTE(migration-compat): corpus.documents M2M is being phased out
        # in favor of DocumentPath-only queries. This line can be removed
        # once all queries have migrated to DocumentPath.
        personal_corpus.documents.add(doc)

        moved_count += 1
        logger.info(
            f"Moved standalone document {doc.pk} to personal corpus "
            f"{personal_corpus.pk} at path {path}"
        )

    logger.info(f"Moved {moved_count} standalone documents to personal corpuses")


def reverse_migration(apps, schema_editor):
    """
    Reverse migration is intentionally blocked to prevent data loss.

    This migration creates personal corpuses and moves standalone documents into them.
    Reversing would:
    - Delete all DocumentPath records linking documents to personal corpuses
    - Delete all personal corpuses
    - Leave documents orphaned with no corpus organization

    If you need to rollback past this migration, you must:
    1. Manually handle document organization
    2. Write a custom migration that preserves document paths
    3. Or accept that documents will become standalone (no corpus)

    For production systems with user data, this is a destructive operation.
    """
    raise NotImplementedError(
        "Migration 0038 (create_personal_corpuses) cannot be reversed automatically. "
        "This migration creates personal corpuses and moves documents into them. "
        "Reversing would delete DocumentPath records and orphan user documents. "
        "If you must rollback, write a custom migration that handles your specific "
        "data preservation requirements. See migration docstring for details."
    )


class Migration(migrations.Migration):
    """
    Data migration to:
    1. Create personal corpuses for existing users
    2. Move standalone documents into their creator's personal corpus
    """

    dependencies = [
        ("corpuses", "0037_add_is_personal_corpus"),
        ("documents", "0030_document_processing_error_and_more"),
        ("users", "0024_setup_telemetry_periodic_task"),
        ("contenttypes", "0002_remove_content_type_name"),
    ]

    operations = [
        migrations.RunPython(
            create_personal_corpuses_and_move_standalone_docs,
            reverse_code=reverse_migration,
        ),
    ]
