package ch.threema.storage.databaseupdate

import ch.threema.base.utils.getThreemaLogger
import net.zetetic.database.sqlcipher.SQLiteDatabase

private val logger = getThreemaLogger("DatabaseUpdateToVersion106")

internal class DatabaseUpdateToVersion106(
    private val sqLiteDatabase: SQLiteDatabase,
) : DatabaseUpdate {
    override fun run() {
        migrateMessageIndices()
        migrateContactReactions()
        migrateGroupReactions()
    }

    private fun migrateMessageIndices() {
        // Recreate message uid indices without UNIQUE requirement
        // Note that the indices `message_uid_idx` and `m_group_message_uid_idx` were created in
        // an earlier version of `DatabaseUpdateToVersion105` as UNIQUE indices which caused that
        // migration to fail on some devices and were thus removed in `DatabaseUpdateToVersion105`

        sqLiteDatabase.execSQL(
            "DROP INDEX IF EXISTS `messageUidIdx`",
        )
        sqLiteDatabase.execSQL(
            "DROP INDEX IF EXISTS `message_uid_idx`",
        )
        sqLiteDatabase.execSQL(
            "CREATE INDEX IF NOT EXISTS `contact_message_uid_idx` ON `message` ( `uid` )",
        )

        sqLiteDatabase.execSQL(
            "DROP INDEX IF EXISTS `groupMessageUidIdx`",
        )
        sqLiteDatabase.execSQL(
            "DROP INDEX IF EXISTS `m_group_message_uid_idx`",
        )
        sqLiteDatabase.execSQL(
            "CREATE INDEX IF NOT EXISTS `group_message_uid_idx` ON `m_group_message` ( `uid` )",
        )
    }

    private fun migrateContactReactions() {
        migrateReactionTables(
            targetReactionTable = "contact_reactions",
            legacyReactionTable = "contact_emoji_reactions",
            messageTable = "message",
            foreignKeyName = "fk_contact_message_id",
            reactionIndexName = "contact_reactions_idx",
            legacyReactionIndexName = "contact_reactions_message_idx",
        )
    }

    private fun migrateGroupReactions() {
        migrateReactionTables(
            targetReactionTable = "group_reactions",
            legacyReactionTable = "group_emoji_reactions",
            messageTable = "m_group_message",
            foreignKeyName = "fk_group_message_id",
            reactionIndexName = "group_reactions_idx",
            legacyReactionIndexName = "group_reactions_message_idx",
        )
    }

    private fun migrateReactionTables(
        targetReactionTable: String,
        legacyReactionTable: String,
        messageTable: String,
        foreignKeyName: String,
        reactionIndexName: String,
        legacyReactionIndexName: String,
    ) {
        sqLiteDatabase.execSQL(
            "CREATE TABLE IF NOT EXISTS `$targetReactionTable` (" +
                "`messageId` INTEGER NOT NULL, " +
                "`senderIdentity` VARCHAR NOT NULL, " +
                "`emojiSequence` VARCHAR NOT NULL, " +
                "`reactedAt` DATETIME NOT NULL, " +
                "UNIQUE(`messageId`,`senderIdentity`,`emojiSequence`) ON CONFLICT REPLACE, " +
                "CONSTRAINT `$foreignKeyName` FOREIGN KEY (`messageId`) " +
                "REFERENCES `$messageTable` (`id`) ON UPDATE CASCADE ON DELETE CASCADE " +
                ")",
        )
        sqLiteDatabase.execSQL("CREATE INDEX `$reactionIndexName` ON `$targetReactionTable` (`messageId`);")

        if (sqLiteDatabase.tableExists(legacyReactionTable)) {
            migrateReactionData(targetReactionTable, legacyReactionTable, messageTable)
        } else {
            logger.info(
                "Skip migrating existing reactions. Legacy reaction table {} does not exist",
                legacyReactionTable,
            )
        }

        // Cleanup potential leftovers from migration to version 105
        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS `$legacyReactionTable`")
        sqLiteDatabase.execSQL("DROP INDEX IF EXISTS `$legacyReactionIndexName`")
    }

    private fun migrateReactionData(
        targetReactionTable: String,
        legacyReactionTable: String,
        messageTable: String,
    ) {
        logger.info(
            "Migrate existing reactions from {} to {}",
            legacyReactionTable,
            targetReactionTable,
        )
        sqLiteDatabase.execSQL(
            "INSERT INTO $targetReactionTable " +
                "SELECT m.id as messageId, r.senderIdentity, r.emojiSequence, r.reactedAt " +
                "FROM $messageTable m JOIN $legacyReactionTable r ON m.uid = r.messageUid",
        )
    }

    override fun getDescription() = "create/migrate reaction tables/indices"

    override val version = 106
}
