from django.db import connection, migrations
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from django.db.migrations.state import StateApps


def fill_new_columns(apps: StateApps, schema_editor: BaseDatabaseSchemaEditor) -> None:
    UserPresence = apps.get_model("zerver", "UserPresence")

    # In theory, we'd like to preserve the distinction between the
    # IDLE and ACTIVE statuses in legacy data.  However, there is no
    # correct way to do so; the previous data structure only stored
    # the current IDLE/ACTIVE status of the last update for each
    # (user, client) pair. There's no way to know whether the last
    # time the user had the other status with that client was minutes
    # or months beforehand.
    #
    # So the only sane thing we can do with this migration is to treat
    # the last presence update as having been a PRESENCE_ACTIVE_STATUS
    # event. This will result in some currently-idle users being
    # incorrectly recorded as having been active at the last moment
    # that they were idle before this migration.  This error is
    # unlikely to be significant in practice, and in any case is an
    # unavoidable flaw caused by the legacy previous data model.
    with connection.cursor() as cursor:
        cursor.execute(
            "SELECT realm_id, user_profile_id, MAX(timestamp) FROM zerver_userpresenceold WHERE status IN (1, 2) GROUP BY realm_id, user_profile_id"
        )
        latest_presence_per_user = cursor.fetchall()

    UserPresence.objects.bulk_create(
        [
            UserPresence(
                user_profile_id=presence_row[1],
                realm_id=presence_row[0],
                last_connected_time=presence_row[2],
                last_active_time=presence_row[2],
            )
            for presence_row in latest_presence_per_user
        ],
        # Limit the size of individual network requests for very large
        # servers.
        batch_size=10000,
        # If the UserPresence worker has already started, or a user
        # has changed their invisible status while migrations are
        # running, then some UserPresence rows may exist. Those will
        # generally be newer than what we have here, so ignoring
        # conflicts so we can complete backfilling users who don't
        # have more current data is the right resolution.
        ignore_conflicts=True,
    )


def clear_new_columns(apps: StateApps, schema_editor: BaseDatabaseSchemaEditor) -> None:
    UserPresence = apps.get_model("zerver", "UserPresence")
    UserPresence.objects.all().delete()


class Migration(migrations.Migration):
    """
    Ports data from the UserPresence model into the new one.
    """

    atomic = False

    dependencies = [
        ("zerver", "0443_userpresence_new_table_schema"),
    ]

    operations = [
        migrations.RunPython(fill_new_columns, reverse_code=clear_new_columns, elidable=True)
    ]
