package de.storchp.fdroidbuildstatus.adapter.db

import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.util.Log
import androidx.core.database.sqlite.transaction
import de.storchp.fdroidbuildstatus.adapter.db.DbConstants.Apps
import de.storchp.fdroidbuildstatus.adapter.db.DbConstants.BuildRuns
import de.storchp.fdroidbuildstatus.adapter.db.DbConstants.Builds
import de.storchp.fdroidbuildstatus.adapter.db.DbConstants.Disabled
import de.storchp.fdroidbuildstatus.adapter.db.DbConstants.NeedsUpdate
import de.storchp.fdroidbuildstatus.adapter.db.DbConstants.Notifications
import de.storchp.fdroidbuildstatus.adapter.db.DbConstants.Versions

internal class DbOpenHelper(context: Context?) :
    SQLiteOpenHelper(context, DbConstants.DATABASE_NAME, null, DATABASE_VERSION) {
        
    override fun onCreate(db: SQLiteDatabase) {
        Log.i(TAG, "Creating database")
        db.execSQL(CREATE_STATEMENT_APPS)
        db.execSQL(CREATE_STATEMENT_BUILD_RUNS)
        db.execSQL(CREATE_STATEMENT_BUILDS)
        db.execSQL(CREATE_STATEMENT_VERSIONS)
        db.execSQL(CREATE_STATEMENT_NOTIFICATIONS)
        Log.i(TAG, "Database structure created.")
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        Log.w(TAG, "Upgrade database from version$oldVersion to $newVersion")
        db.transaction {
            if (oldVersion < 2) {
                execSQL("ALTER TABLE ${DbConstants.DATABASE_TABLE_APPS} ADD COLUMN ${Apps.NAME} TEXT")
            }
            if (oldVersion < 3) {
                execSQL(CREATE_STATEMENT_BUILD_RUNS)
                execSQL(CREATE_STATEMENT_BUILDS)
            }
            if (oldVersion == 3) {
                execSQL("ALTER TABLE ${DbConstants.DATABASE_TABLE_BUILD_RUNS} ADD COLUMN ${BuildRuns.MAX_BUILD_TIME_REACHED} INTEGER")
            }
            if (oldVersion in 3..4) {
                execSQL("ALTER TABLE ${DbConstants.DATABASE_TABLE_BUILD_RUNS} ADD COLUMN ${BuildRuns.SUBCOMMAND} TEXT")
            }
            if (oldVersion in 3..5) {
                execSQL("ALTER TABLE ${DbConstants.DATABASE_TABLE_BUILD_RUNS} ADD COLUMN ${BuildRuns.DATA_COMMIT_ID} TEXT")
            }
            if (oldVersion in 3..6) {
                execSQL("ALTER TABLE ${DbConstants.DATABASE_TABLE_BUILDS} ADD COLUMN sourceCode TEXT")
            }
            if (oldVersion < 9) {
                if (!isTableExists(this, DbConstants.DATABASE_TABLE_DISABLED)) {
                    execSQL(CREATE_STATEMENT_DISABLED)
                }
                if (!isTableExists(this, DbConstants.DATABASE_TABLE_NEEDS_UPDATE)) {
                    execSQL(CREATE_STATEMENT_NEEDS_UPDATE)
                }
            }
            if (oldVersion < 10) {
                execSQL("ALTER TABLE ${DbConstants.DATABASE_TABLE_APPS} ADD COLUMN ${Apps.DISABLED} INTEGER")
                execSQL("ALTER TABLE ${DbConstants.DATABASE_TABLE_APPS} ADD COLUMN ${Apps.NEEDS_UPDATE} INTEGER")
                execSQL("UPDATE ${DbConstants.DATABASE_TABLE_APPS} SET ${Apps.DISABLED} = 1 WHERE ${Apps.ID} IN (SELECT ${Disabled.ID} FROM ${DbConstants.DATABASE_TABLE_DISABLED})")
                execSQL("UPDATE ${DbConstants.DATABASE_TABLE_APPS} SET ${Apps.NEEDS_UPDATE} = 1 WHERE ${Apps.ID} IN (SELECT ${NeedsUpdate.ID} FROM ${DbConstants.DATABASE_TABLE_NEEDS_UPDATE})")
                if (isTableExists(this, DbConstants.DATABASE_TABLE_DISABLED)) {
                    execSQL("DROP TABLE ${DbConstants.DATABASE_TABLE_DISABLED}")
                }
                if (isTableExists(this, DbConstants.DATABASE_TABLE_NEEDS_UPDATE)) {
                    execSQL("DROP TABLE ${DbConstants.DATABASE_TABLE_NEEDS_UPDATE}")
                }
            }
            if (oldVersion < 11) {
                execSQL(CREATE_STATEMENT_NOTIFICATIONS)
            }
            if (oldVersion < 12) {
                execSQL("ALTER TABLE ${DbConstants.DATABASE_TABLE_APPS} ADD COLUMN ${Apps.SOURCE_CODE} TEXT")
            }
            if (oldVersion < 13) {
                execSQL(CREATE_STATEMENT_VERSIONS)
                execSQL(
                    """
                        INSERT INTO ${DbConstants.DATABASE_TABLE_VERSIONS} (${Versions.ID}, ${Versions.VERSION_CODE}, ${Versions.VERSION_NAME}) 
                            SELECT ${Builds.ID}, ${Builds.VERSION_CODE}, MAX(${Builds.VERSION}) 
                            FROM ${DbConstants.DATABASE_TABLE_BUILDS} 
                            WHERE ${Builds.VERSION} IS NOT NULL GROUP BY ${Builds.ID}, ${Builds.VERSION_CODE}
                        
                    """.trimMargin()
                )
            }
            if (oldVersion < 14) {
                execSQL("ALTER TABLE ${DbConstants.DATABASE_TABLE_APPS} ADD COLUMN ${Apps.ARCHIVED} INTEGER")
                execSQL("ALTER TABLE ${DbConstants.DATABASE_TABLE_APPS} ADD COLUMN ${Apps.NO_PACKAGES} INTEGER")
                execSQL("ALTER TABLE ${DbConstants.DATABASE_TABLE_APPS} ADD COLUMN ${Apps.NO_UPDATE_CHECK} INTEGER")
            }
        }
    }

    private fun isTableExists(db: SQLiteDatabase, tableName: String): Boolean {
        db.rawQuery(
            "SELECT DISTINCT tbl_name FROM sqlite_master WHERE tbl_name = ?",
            arrayOf(tableName)
        ).use { cursor ->
            return cursor.count > 0
        }
    }
}

private val TAG = DbOpenHelper::class.java.simpleName
private const val DATABASE_VERSION = 14

private const val CREATE_STATEMENT_APPS =
    ("""
        CREATE TABLE ${DbConstants.DATABASE_TABLE_APPS} (
            ${Apps.ID} TEXT PRIMARY KEY,
            ${Apps.NAME} TEXT,
            ${Apps.FAVOURITE} INTEGER,
            ${Apps.DISABLED} INTEGER,
            ${Apps.NEEDS_UPDATE} INTEGER,
            ${Apps.SOURCE_CODE} TEXT,
            ${Apps.ARCHIVED} INTEGER,
            ${Apps.NO_PACKAGES} INTEGER,
            ${Apps.NO_UPDATE_CHECK} INTEGER
        )
    """)

private const val CREATE_STATEMENT_BUILD_RUNS =
    ("""
        CREATE TABLE ${DbConstants.DATABASE_TABLE_BUILD_RUNS} (
            ${BuildRuns.BUILD_RUN_TYPE} TEXT PRIMARY KEY,
            ${BuildRuns.START} INTEGER,
            "${BuildRuns.END}" INTEGER,
            ${BuildRuns.LAST_MODIFIED} INTEGER,
            ${BuildRuns.LAST_UPDATED} INTEGER,
            ${BuildRuns.MAX_BUILD_TIME_REACHED} INTEGER,
            ${BuildRuns.SUBCOMMAND} TEXT,
            ${BuildRuns.DATA_COMMIT_ID} TEXT
        )
    """)

private const val CREATE_STATEMENT_BUILDS =
    ("""
        CREATE TABLE ${DbConstants.DATABASE_TABLE_BUILDS} (
            ${Builds.BUILD_RUN_TYPE} TEXT,
            ${Builds.ID} TEXT,
            ${Builds.VERSION_CODE} TEXT,
            ${Builds.STATUS} TEXT,
            ${Builds.LOG} TEXT,
            PRIMARY KEY (${Builds.BUILD_RUN_TYPE}, ${Builds.ID}, ${Builds.VERSION_CODE})
        )
    """)

private const val CREATE_STATEMENT_VERSIONS =
    ("""
        CREATE TABLE ${DbConstants.DATABASE_TABLE_VERSIONS} (
            ${Versions.ID} TEXT,
            ${Versions.VERSION_CODE} TEXT,
            ${Versions.VERSION_NAME} TEXT,
            PRIMARY KEY (${Versions.ID}, ${Versions.VERSION_CODE})
        )
    """)

private const val CREATE_STATEMENT_DISABLED =
    ("CREATE TABLE ${DbConstants.DATABASE_TABLE_DISABLED} (${Apps.ID} TEXT PRIMARY KEY)")

private const val CREATE_STATEMENT_NEEDS_UPDATE =
    ("CREATE TABLE ${DbConstants.DATABASE_TABLE_NEEDS_UPDATE} (${Apps.ID} TEXT PRIMARY KEY)")

private const val CREATE_STATEMENT_NOTIFICATIONS =
    ("""
        CREATE TABLE ${DbConstants.DATABASE_TABLE_NOTIFICATIONS} (
            ${Notifications.BUILD_RUN_TYPE} TEXT,
            ${Notifications.START} INTEGER,
            ${Notifications.ID} TEXT,
            ${Notifications.VERSION_CODE} TEXT,
            PRIMARY KEY (${Notifications.BUILD_RUN_TYPE}, ${Notifications.START}, ${Notifications.ID}, ${Notifications.VERSION_CODE})
        )
    """)
