package com.github.mondei1.offpass.entities

import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.util.Base64
import android.util.Log
import com.github.mondei1.offpass.CryptoOperations
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.nio.charset.Charset
import kotlin.system.measureTimeMillis

class CompressionHelper(context: Context, factory: SQLiteDatabase.CursorFactory?):
    SQLiteOpenHelper(context, DATABASE_NAME, factory, DATABASE_VERSION) {

    private fun init_db(db: SQLiteDatabase) {
        //db.execSQL("DROP TABLE $TABLE_NAME")
        db.execSQL(
            "CREATE TABLE IF NOT EXISTS `$TABLE_NAME` " +
                    "(`$COLUMN_KEY` VARCHAR(4) PRIMARY KEY, `$COLUMN_VALUE` TEXT, `$COLUMN_IV` BLOB," +
                    " `$COLUMN_SALT` BLOB)"
        )
    }

    override fun onCreate(db: SQLiteDatabase) {
        this.init_db(db)
    }

    override fun onOpen(db: SQLiteDatabase) {
        super.onOpen(db)
        this.init_db(db)
    }

    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
        // Do nothing. We don't want to delete any sensitive values.
    }

    fun add(session_key: String, compression: Compression) {
        val encrypted_result = GlobalScope.async {
            CryptoOperations().encrypt(session_key, compression.value!!)
        }

        runBlocking {
            val res = encrypted_result.await()
            writableDatabase.execSQL("INSERT INTO $TABLE_NAME (`$COLUMN_KEY`, `$COLUMN_VALUE`, `$COLUMN_IV`, `$COLUMN_SALT`) " +
                    "VALUES (?, ?, ?, ?)",
                arrayOf(compression.key, res.result, res.iv, res.salt))
            Log.d("Compression Helper", "Add key ${compression.key} = $res to database.")
        }
    }

    /**
     * @return The encrypted content and the IV used for encryption of a specified key.
     * The returned array looks like that: [key, encrypted_value, iv]
     */
    suspend fun get(session_key: String, key: String): String? {
        var cursor = this.readableDatabase.rawQuery("SELECT * FROM $TABLE_NAME WHERE `key`=?", arrayOf(key))
        var list: ArrayList<String> = ArrayList<String>()
        var salt: ByteArray = ByteArray(16)
        var iv: ByteArray = ByteArray(16)

        with(cursor) {
            while (moveToNext()) {
                list.add(getString(getColumnIndex(COLUMN_VALUE)))
                iv = getBlob(getColumnIndex(COLUMN_IV))
                salt = getBlob(getColumnIndex(COLUMN_SALT))
            }
        }

        val decrypted_result = GlobalScope.async {
            // TODO: Is a list even required?
            CryptoOperations().decrypt(list[0], session_key, iv, salt)
        }

        return decrypted_result.await().toString()
    }

    companion object {
        private val DATABASE_VERSION = 3
        private val DATABASE_NAME = "compression.db"
        val TABLE_NAME = "compression"
        val COLUMN_KEY = "key"
        val COLUMN_VALUE = "value"
        val COLUMN_SALT = ""
        val COLUMN_IV = "iv"
    }
}