# Generated by Django 4.2.16 on 2024-12-04 21:17

import logging

from django.db import migrations
from django.db.models import Subquery, OuterRef

logger = logging.getLogger(__name__)


def drop_orphaned_slack_messages(apps, schema_editor):
    """
    At this point, in the 0007_migrate_slackmessage_channel_id migration, we have already migrated the old
    SlackMessage._channel_id (char field) to the new SlackMessage.channel_id (FK field).

    This script is needed to identity, and drop, SlackMessage records that are "orphaned". This would've occured in two
    cases:
    - the organization switched their Slack team identity. We have historically never done clean-up of the old SlackMessage records
    - a Slack channel was deleted. Previously, we did not have a FK constraint on the SlackMessage._channel_id
    char field, so there was no CASCADE delete behaviour (now there is).

    In both of these scenarios, the SlackMessage records would be "orphaned" - they would have a non-null channel_id
    foreign key relationship, which prevents us from setting NOT NULL constraint on the channel_id field.
    """
    SlackMessage = apps.get_model("slack", "SlackMessage")
    ShiftSwapRequest = apps.get_model("schedules", "ShiftSwapRequest")

    logger.info("Starting migration to drop orphaned SlackMessage records.")

    slack_messages = SlackMessage.objects.filter(channel__isnull=True)

    total_messages = slack_messages.count()
    if total_messages == 0:
        logger.info("No orphaned SlackMessages need to be dropped.")
        return

    logger.info(f"Found {total_messages} orphaned SlackMessages to drop.")

    # Set the referencing ShiftSwapRequests' slack_message_id field to NULL (only for orphaned SlackMessages)
    ShiftSwapRequest.objects.filter(slack_message__in=slack_messages).update(slack_message=None)
    logger.info("Unset slack_message references from ShiftSwapRequests.")

    slack_messages.delete()

    logger.info("Finished migration to drop orphaned SlackMessages.")


def fill_in_missing_slack_team_identity_values(apps, schema_editor):
    """
    This migration fills in the missing `slack_team_identity` field for SlackMessage records that were
    created before the field was added. This field is required for the SlackMessage model to be valid, and it is
    necessary for the SlackMessage model to be associated with a SlackChannel model.
    """
    SlackMessage = apps.get_model("slack", "SlackMessage")
    SlackChannel = apps.get_model("slack", "SlackChannel")

    logger.info("Starting migration to fill in missing SlackMessage.slack_team_identity values")

    if schema_editor.connection.vendor == "mysql":
        # Use raw SQL for MySQL
        with schema_editor.connection.cursor() as cursor:
            # Update SlackMessages by joining with SlackChannel
            sql_update = """
            UPDATE slack_slackmessage sm
            JOIN slack_slackchannel sc ON sm.channel_id = sc.id
            SET sm.slack_team_identity = sc.slack_team_identity_id
            WHERE sm.slack_team_identity IS NULL
              AND sm.channel_id IS NOT NULL
              AND sc.slack_team_identity_id IS NOT NULL;
            """
            cursor.execute(sql_update)
            updated_rows = cursor.rowcount  # Number of rows updated

            logger.info(f"Updated {updated_rows} SlackMessages with slack_team_identity from slack_channel.")

            # Count remaining messages that couldn't be updated
            cursor.execute("""
            SELECT COUNT(*)
            FROM slack_slackmessage sm
            LEFT JOIN slack_slackchannel sc ON sm.channel_id = sc.id
            WHERE sm.slack_team_identity IS NULL
              AND (sm.channel_id IS NULL OR sc.slack_team_identity_id IS NULL);
            """)
            remaining_count = cursor.fetchone()[0]

            if remaining_count > 0:
                logger.warning(
                    f"{remaining_count} SlackMessages could not be updated because slack_channel or slack_channel.slack_team_identity is missing."
                )
    else:
        # Use Django ORM for other databases
        slack_messages = SlackMessage.objects.filter(_slack_team_identity__isnull=True)

        total_messages = slack_messages.count()
        if total_messages == 0:
            logger.info("No missing SlackMessage.slack_team_identity values")
            return

        logger.info(f"Found {total_messages} SlackMessages which have missing slack_team_identity values.")

        # Subquery to get slack_team_identity from SlackChannel
        channel_team_identity_subquery = SlackChannel.objects.filter(
            pk=OuterRef('channel_id')
        ).values('slack_team_identity_id')[:1]

        # Update from slack_channel
        updated_count = slack_messages.filter(
            channel__isnull=False,
            channel__slack_team_identity__isnull=False
        ).update(
            _slack_team_identity=Subquery(channel_team_identity_subquery)
        )

        logger.info(f"Updated {updated_count} SlackMessages with slack_team_identity from slack_channel.")

        # Check if there are any SlackMessages that couldn't be updated
        remaining_messages = SlackMessage.objects.filter(_slack_team_identity__isnull=True)
        remaining_count = remaining_messages.count()

        if remaining_count > 0:
            logger.warning(
                f"{remaining_count} SlackMessages could not be updated because slack_channel or slack_channel.slack_team_identity is missing."
            )

    logger.info("Finished migration to fill in missing SlackMessage.slack_team_identity values")


class Migration(migrations.Migration):

    dependencies = [
        ('slack', '0008_remove_slackmessage_active_update_task_id_state'),
    ]

    operations = [
        migrations.RunPython(drop_orphaned_slack_messages, migrations.RunPython.noop),
        migrations.RunPython(fill_in_missing_slack_team_identity_values, migrations.RunPython.noop),
    ]
