package ch.threema.storage.databaseupdate

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

private val logger = getThreemaLogger("DatabaseUpdateToVersion89")

internal class DatabaseUpdateToVersion89(
    private val sqLiteDatabase: SQLiteDatabase,
) : DatabaseUpdate {

    override fun run() {
        val tables = arrayOf("contacts", "m_group", "distribution_list")
        val field = "lastUpdate"

        // Add field
        for (table in tables) {
            if (!sqLiteDatabase.fieldExists(table, field)) {
                logger.info("Adding $field field to table $table")
                sqLiteDatabase.execSQL("ALTER TABLE `$table` ADD COLUMN `$field` INTEGER")
            }
        }

        // Calculate lastUpdate based on existing data
        calculateLastUpdateContacts()
        calculateLastUpdateGroups()
        calculateLastUpdateDistributionLists()
    }

    private fun calculateLastUpdateContacts() {
        logger.info("Calculate lastUpdate for contacts")

        // Consider all message types except date separators and forward security status messages.
        // Note that in a previous version of the update script (that has been applied for most
        // users), all message types have been used to determine the last update flag leading to
        // some chat reordering.
        sqLiteDatabase.execSQL(
            """
            UPDATE contacts
            SET lastUpdate = tmp.lastUpdate FROM (
                SELECT m.identity, max(m.createdAtUtc) as lastUpdate
                FROM message m
                WHERE m.isSaved = 1 AND type != 10 AND type != 12
                GROUP BY m.identity
            ) tmp
            WHERE contacts.identity = tmp.identity;
        """,
        )
    }

    private fun calculateLastUpdateGroups() {
        logger.info("Calculate lastUpdate for groups")

        // Set lastUpdate to the create date of the latest message if present
        // Consider all message types except date separators and group status messages. Note that in
        // a previous version of the update script (that has been applied for most users), all
        // message types have been used to determine the last update flag leading to some chat
        // reordering.
        sqLiteDatabase.execSQL(
            """
            UPDATE m_group
            SET lastUpdate = tmp.lastUpdate FROM (
                SELECT m.groupId, max(m.createdAtUtc) as lastUpdate
                FROM m_group_message m
                WHERE m.isSaved = 1 AND type != 10 AND type != 13
                GROUP BY m.groupId
            ) tmp
            WHERE m_group.id = tmp.groupId;
        """,
        )

        // Set lastUpdate for groups without messages.
        // `createdAt` is stored in localtime and therefore needs to be converted to UTC.
        sqLiteDatabase.execSQL(
            """
            UPDATE m_group
            SET lastUpdate = strftime('%s', createdAt, 'utc') * 1000
            WHERE lastUpdate IS NULL;
        """,
        )
    }

    private fun calculateLastUpdateDistributionLists() {
        logger.info("Calculate lastUpdate for distribution lists")

        // Set lastUpdate to the create date of the latest message if present
        sqLiteDatabase.execSQL(
            """
            UPDATE distribution_list
            SET lastUpdate = tmp.lastUpdate FROM (
                SELECT m.distributionListId, max(m.createdAtUtc) as lastUpdate
                FROM distribution_list_message m
                WHERE m.isSaved = 1
                GROUP BY m.distributionListId
            ) tmp
            WHERE distribution_list.id = tmp.distributionListId;
        """,
        )

        // Set lastUpdate for distribution lists without messages.
        // `createdAt` is stored in localtime and therefore needs to be converted to UTC.
        sqLiteDatabase.execSQL(
            """
            UPDATE distribution_list
            SET lastUpdate = strftime('%s', createdAt, 'utc') * 1000
            WHERE lastUpdate IS NULL;
        """,
        )
    }

    override fun getDescription() = "add lastUpdate field"

    override val version = 89
}
