package pl.firmatpp.kierowca.data.upload

import android.content.Context
import androidx.room.migration.Migration
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import pl.firmatpp.kierowca.data.sync.DriverBootstrapCacheEntity
import pl.firmatpp.kierowca.data.sync.DriverCacheDao
import pl.firmatpp.kierowca.data.sync.DriverRouteCacheEntity
import pl.firmatpp.kierowca.data.sync.DriverSyncStateEntity

@Database(
    entities = [
        PhotoUploadEntity::class,
        DispatchSheetUploadEntity::class,
        RouteActionEntity::class,
        RoutePointEntity::class,
        DriverBootstrapCacheEntity::class,
        DriverRouteCacheEntity::class,
        DriverSyncStateEntity::class,
    ],
    version = 5,
    exportSchema = false,
)
abstract class DriverDatabase : RoomDatabase() {
    abstract fun photoUploadDao(): PhotoUploadDao
    abstract fun dispatchSheetUploadDao(): DispatchSheetUploadDao
    abstract fun routeActionDao(): RouteActionDao
    abstract fun routePointDao(): RoutePointDao
    abstract fun driverCacheDao(): DriverCacheDao

    companion object {
        @Volatile
        private var instance: DriverDatabase? = null

        private val migration1To2 = object : Migration(1, 2) {
            override fun migrate(db: SupportSQLiteDatabase) {
                db.execSQL(
                    """
                    CREATE TABLE IF NOT EXISTS driver_bootstrap_cache (
                        date TEXT NOT NULL PRIMARY KEY,
                        payloadJson TEXT NOT NULL,
                        checksum TEXT,
                        version INTEGER,
                        syncedAtEpochMillis INTEGER NOT NULL
                    )
                    """.trimIndent(),
                )
                db.execSQL(
                    """
                    CREATE TABLE IF NOT EXISTS driver_route_cache (
                        routeId TEXT NOT NULL PRIMARY KEY,
                        payloadJson TEXT NOT NULL,
                        checksum TEXT,
                        version INTEGER,
                        syncedAtEpochMillis INTEGER NOT NULL
                    )
                    """.trimIndent(),
                )
                db.execSQL(
                    """
                    CREATE TABLE IF NOT EXISTS driver_sync_states (
                        key TEXT NOT NULL PRIMARY KEY,
                        scope TEXT NOT NULL,
                        date TEXT,
                        routeId TEXT,
                        checksum TEXT NOT NULL,
                        version INTEGER NOT NULL,
                        computedAt TEXT NOT NULL,
                        syncedAtEpochMillis INTEGER NOT NULL
                    )
                    """.trimIndent(),
                )
                db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS index_driver_sync_states_scope_date_routeId ON driver_sync_states(scope, date, routeId)")
            }
        }

        private val migration2To3 = object : Migration(2, 3) {
            override fun migrate(db: SupportSQLiteDatabase) {
                db.execSQL(
                    """
                    CREATE TABLE IF NOT EXISTS dispatch_sheet_uploads (
                        clientRequestId TEXT NOT NULL PRIMARY KEY,
                        workDate TEXT NOT NULL,
                        replacePhotoId TEXT,
                        localPath TEXT NOT NULL,
                        source TEXT NOT NULL,
                        takenAt TEXT,
                        latitude REAL,
                        longitude REAL,
                        locationAccuracyMeters REAL,
                        mimeType TEXT NOT NULL,
                        size INTEGER NOT NULL,
                        contentSha256 TEXT NOT NULL,
                        status TEXT NOT NULL,
                        progress INTEGER NOT NULL,
                        attemptCount INTEGER NOT NULL,
                        lastError TEXT,
                        serverPhotoId TEXT,
                        createdAtEpochMillis INTEGER NOT NULL,
                        updatedAtEpochMillis INTEGER NOT NULL
                    )
                    """.trimIndent(),
                )
                db.execSQL("CREATE INDEX IF NOT EXISTS index_dispatch_sheet_uploads_workDate ON dispatch_sheet_uploads(workDate)")
                db.execSQL("CREATE INDEX IF NOT EXISTS index_dispatch_sheet_uploads_status ON dispatch_sheet_uploads(status)")
            }
        }

        private val migration3To4 = object : Migration(3, 4) {
            override fun migrate(db: SupportSQLiteDatabase) {
                db.execSQL("ALTER TABLE photo_uploads ADD COLUMN stage TEXT NOT NULL DEFAULT 'other'")
            }
        }

        private val migration4To5 = object : Migration(4, 5) {
            override fun migrate(db: SupportSQLiteDatabase) {
                db.execSQL(
                    """
                    CREATE TABLE IF NOT EXISTS route_actions (
                        clientActionId TEXT NOT NULL PRIMARY KEY,
                        routeId TEXT NOT NULL,
                        action TEXT NOT NULL,
                        weight REAL NOT NULL,
                        occurredAt TEXT NOT NULL,
                        photoClientRequestIdsJson TEXT NOT NULL,
                        status TEXT NOT NULL,
                        attemptCount INTEGER NOT NULL,
                        lastError TEXT,
                        createdAtEpochMillis INTEGER NOT NULL,
                        updatedAtEpochMillis INTEGER NOT NULL
                    )
                    """.trimIndent(),
                )
                db.execSQL("CREATE INDEX IF NOT EXISTS index_route_actions_routeId ON route_actions(routeId)")
                db.execSQL("CREATE INDEX IF NOT EXISTS index_route_actions_status ON route_actions(status)")
                db.execSQL(
                    """
                    CREATE TABLE IF NOT EXISTS route_points (
                        clientPointId TEXT NOT NULL PRIMARY KEY,
                        routeId TEXT NOT NULL,
                        driverId TEXT NOT NULL,
                        lat REAL NOT NULL,
                        lon REAL NOT NULL,
                        accuracy REAL NOT NULL,
                        speed REAL,
                        bearing REAL,
                        timestamp INTEGER NOT NULL,
                        battery INTEGER,
                        charging INTEGER NOT NULL,
                        status TEXT NOT NULL,
                        attemptCount INTEGER NOT NULL,
                        lastError TEXT,
                        createdAtEpochMillis INTEGER NOT NULL,
                        updatedAtEpochMillis INTEGER NOT NULL
                    )
                    """.trimIndent(),
                )
                db.execSQL("CREATE INDEX IF NOT EXISTS index_route_points_routeId ON route_points(routeId)")
                db.execSQL("CREATE INDEX IF NOT EXISTS index_route_points_status ON route_points(status)")
                db.execSQL("CREATE INDEX IF NOT EXISTS index_route_points_routeId_status ON route_points(routeId, status)")
            }
        }

        fun get(context: Context): DriverDatabase =
            instance ?: synchronized(this) {
                instance ?: Room.databaseBuilder(
                    context.applicationContext,
                    DriverDatabase::class.java,
                    "driver-local-outbox.db",
                ).addMigrations(migration1To2, migration2To3, migration3To4, migration4To5).build().also { instance = it }
            }
    }
}
