# Generated by Django 5.2.11 on 2026-02-25 17:45

import django.db.migrations.operations.special
from django.db import migrations, models


def populate_read_at(apps, schema_editor):
    """Populate read_at for existing ThreadAccess entries."""
    ThreadAccess = apps.get_model("core", "ThreadAccess")
    Message = apps.get_model("core", "Message")

    thread_accesses = ThreadAccess.objects.all().iterator(chunk_size=1000)

    batch = []
    for access in thread_accesses:
        # Find the most recent read message in this thread.
        # We rely on ``read_at IS NOT NULL`` instead of ``is_unread=False``
        # because ``is_unread`` defaults to ``False``, so messages that were
        # never explicitly read would incorrectly match.
        last_read_message = (
            Message.objects.filter(thread_id=access.thread_id, is_unread=False)
            .order_by("-created_at")
            .values_list("created_at", flat=True)
            .first()
        )

        if last_read_message is not None:
            access.read_at = last_read_message
            batch.append(access)

        if len(batch) >= 500:
            ThreadAccess.objects.bulk_update(batch, ["read_at"])
            batch = []

    if batch:
        ThreadAccess.objects.bulk_update(batch, ["read_at"])

def populate_per_view_messaged_at(apps, schema_editor):
    """Populate the 5 per-view messaged_at fields for all existing threads."""
    Thread = apps.get_model("core", "Thread")
    Message = apps.get_model("core", "Message")

    threads = Thread.objects.all().iterator(chunk_size=500)

    batch = []
    for thread in threads:
        messages = list(
            Message.objects.filter(thread_id=thread.id)
            .values(
                "is_trashed",
                "is_draft",
                "is_sender",
                "is_spam",
                "is_archived",
                "created_at",
            )
            .order_by("created_at")
        )

        if not messages:
            continue

        # Active: received messages, not spam/archived/trashed/draft
        active_msgs = [
            m
            for m in messages
            if not m["is_sender"]
            and not m["is_spam"]
            and not m["is_archived"]
            and not m["is_trashed"]
            and not m["is_draft"]
        ]
        thread.active_messaged_at = (
            max(m["created_at"] for m in active_msgs) if active_msgs else None
        )

        # Trashed
        trashed_msgs = [m for m in messages if m["is_trashed"]]
        thread.trashed_messaged_at = (
            max(m["created_at"] for m in trashed_msgs) if trashed_msgs else None
        )

        # Draft (non-trashed)
        draft_msgs = [
            m for m in messages if m["is_draft"] and not m["is_trashed"]
        ]
        thread.draft_messaged_at = (
            max(m["created_at"] for m in draft_msgs) if draft_msgs else None
        )

        # Sender (non-trashed, non-draft)
        sender_msgs = [
            m
            for m in messages
            if m["is_sender"] and not m["is_trashed"] and not m["is_draft"]
        ]
        thread.sender_messaged_at = (
            max(m["created_at"] for m in sender_msgs) if sender_msgs else None
        )

        # Archived (non-trashed)
        archived_msgs = [
            m for m in messages if m["is_archived"] and not m["is_trashed"]
        ]
        thread.archived_messaged_at = (
            max(m["created_at"] for m in archived_msgs)
            if archived_msgs
            else None
        )

        batch.append(thread)

        if len(batch) >= 500:
            Thread.objects.bulk_update(
                batch,
                [
                    "active_messaged_at",
                    "trashed_messaged_at",
                    "draft_messaged_at",
                    "sender_messaged_at",
                    "archived_messaged_at",
                ],
            )
            batch = []

    if batch:
        Thread.objects.bulk_update(
            batch,
            [
                "active_messaged_at",
                "trashed_messaged_at",
                "draft_messaged_at",
                "sender_messaged_at",
                "archived_messaged_at",
            ],
        )

class Migration(migrations.Migration):

    dependencies = [
        ('core', '0017_message_sender_user'),
    ]

    operations = [
        migrations.AddField(
            model_name='threadaccess',
            name='read_at',
            field=models.DateTimeField(blank=True, null=True, verbose_name='read at'),
        ),
        migrations.RunPython(
            code=populate_read_at,
            reverse_code=django.db.migrations.operations.special.RunPython.noop,
        ),
        migrations.AlterField(
            model_name='thread',
            name='messaged_at',
            field=models.DateTimeField(blank=True, db_index=True, null=True, verbose_name='messaged at'),
        ),
        migrations.AddField(
            model_name='thread',
            name='active_messaged_at',
            field=models.DateTimeField(blank=True, db_index=True, help_text='Date of the last active (received, not spam/archived/trashed/draft) message.', null=True, verbose_name='active messaged at'),
        ),
        migrations.AddField(
            model_name='thread',
            name='trashed_messaged_at',
            field=models.DateTimeField(blank=True, db_index=True, help_text='Date of the last trashed message.', null=True, verbose_name='trashed messaged at'),
        ),
        migrations.AddField(
            model_name='thread',
            name='draft_messaged_at',
            field=models.DateTimeField(blank=True, db_index=True, help_text='Date of the last draft (non-trashed) message.', null=True, verbose_name='draft messaged at'),
        ),
        migrations.AddField(
            model_name='thread',
            name='sender_messaged_at',
            field=models.DateTimeField(blank=True, db_index=True, help_text='Date of the last sent (non-trashed, non-draft) message.', null=True, verbose_name='sender messaged at'),
        ),
        migrations.AddField(
            model_name='thread',
            name='archived_messaged_at',
            field=models.DateTimeField(blank=True, db_index=True, help_text='Date of the last archived (non-trashed) message.', null=True, verbose_name='archived messaged at'),
        ),
        migrations.RunPython(
            code=populate_per_view_messaged_at,
            reverse_code=django.db.migrations.operations.special.RunPython.noop,
        ),
    ]
