package net.hivetechnology.o20010_06.storage

import android.content.Context
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.util.Base64
import net.hivetechnology.o20010_06.model.SessionCredentials
import org.json.JSONObject
import java.security.KeyStore
import java.util.UUID
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.GCMParameterSpec

class SecureSessionStore(context: Context) {
    private val preferences = context.getSharedPreferences("mobile_vpn_state", Context.MODE_PRIVATE)
    private val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }

    val installationId: String
        get() = preferences.getString(INSTALLATION_ID, null) ?: UUID.randomUUID().toString().also {
            preferences.edit().putString(INSTALLATION_ID, it).apply()
        }

    fun save(credentials: SessionCredentials) {
        val cipher = Cipher.getInstance(TRANSFORMATION)
        cipher.init(Cipher.ENCRYPT_MODE, key())
        val ciphertext = cipher.doFinal(credentials.toJson().toString().toByteArray(Charsets.UTF_8))
        preferences.edit()
            .putString(SESSION_IV, Base64.encodeToString(cipher.iv, Base64.NO_WRAP))
            .putString(SESSION_DATA, Base64.encodeToString(ciphertext, Base64.NO_WRAP))
            .apply()
    }

    fun load(): SessionCredentials? = runCatching {
        val iv = Base64.decode(preferences.getString(SESSION_IV, null) ?: return null, Base64.NO_WRAP)
        val data = Base64.decode(preferences.getString(SESSION_DATA, null) ?: return null, Base64.NO_WRAP)
        val cipher = Cipher.getInstance(TRANSFORMATION)
        cipher.init(Cipher.DECRYPT_MODE, key(), GCMParameterSpec(128, iv))
        SessionCredentials.fromJson(JSONObject(String(cipher.doFinal(data), Charsets.UTF_8)))
    }.getOrNull()

    fun clear() {
        preferences.edit().remove(SESSION_IV).remove(SESSION_DATA).apply()
    }

    private fun key(): SecretKey {
        (keyStore.getKey(KEY_ALIAS, null) as? SecretKey)?.let { return it }
        val generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
        generator.init(
            KeyGenParameterSpec.Builder(KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .setRandomizedEncryptionRequired(true)
                .build(),
        )
        return generator.generateKey()
    }

    companion object {
        private const val KEY_ALIAS = "ht_portal_mobile_vpn_session"
        private const val TRANSFORMATION = "AES/GCM/NoPadding"
        private const val INSTALLATION_ID = "installation_id"
        private const val SESSION_IV = "session_iv"
        private const val SESSION_DATA = "session_data"
    }
}

