package net.buzzert.kordophonedroid.ui.shared

import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import net.buzzert.kordophone.backend.server.Authentication
import java.lang.reflect.Constructor
import javax.inject.Inject
import javax.inject.Singleton


data class ServerConfig(
    var serverName: String? = null,
    var authentication: ServerAuthentication? = null,
) {
    companion object {
        private const val SHARED_PREF_NAME = "KordophonePreferences"

        fun loadFromSettings(context: Context): ServerConfig {
            val prefs = getSharedPreferences(context)
            return ServerConfig(
                serverName = prefs.getString("serverName", null).normalizedBaseUrl(),
                authentication = ServerAuthentication.loadFromEncryptedSettings(context)
            )
        }

        private fun getSharedPreferences(context: Context): SharedPreferences {
            return context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
        }
    }

    fun saveToSettings(context: Context) {
        val prefs = getSharedPreferences(context)
        prefs.edit {
            putString("serverName", serverName.normalizedBaseUrl())
            apply()
        }

        authentication?.saveToEncryptedSettings(context)
    }
}

fun String?.normalizedBaseUrl(): String? {
    val value = this?.trim()?.takeIf { it.isNotEmpty() } ?: return null
    return if (value.endsWith("/")) value else "$value/"
}

data class ServerAuthentication(
    val username: String,
    val password: String,
) {
    companion object {
        fun loadFromEncryptedSettings(context: Context): ServerAuthentication? {
            val prefs = getEncryptedSharedPreferences(context)

            val username = prefs.getString("username", null)
            val password = prefs.getString("password", null)
            if (username != null && password != null) {
                return ServerAuthentication(username, password)
            }

            return null
        }

        private fun getEncryptedSharedPreferences(context: Context): SharedPreferences {
            val masterKey = MasterKey.Builder(context)
                .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
                .build()

            return EncryptedSharedPreferences(
                context,
                "secrets",
                masterKey,
                EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
                EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
            )
        }
    }

    fun saveToEncryptedSettings(context: Context) {
        val prefs = getEncryptedSharedPreferences(context)
        prefs.edit {
            putString("username", username)
            putString("password", password)
            apply()
        }
    }

    fun toBackendAuthentication(): Authentication {
        return Authentication(username, password)
    }
}

@Singleton
class ServerConfigRepository @Inject constructor(
    @ApplicationContext val context: Context,
) {
    // TODO: Initial config should be loaded from device settings.
    private val _serverConfig = MutableStateFlow(ServerConfig.loadFromSettings(context)) // Initial config
    val serverConfig: StateFlow<ServerConfig> = _serverConfig

    fun applyConfig(applicator: ServerConfig.() -> Unit) {
        val config = _serverConfig.value.copy()
        _serverConfig.value = config.apply(applicator).also {
            it.serverName = it.serverName.normalizedBaseUrl()
        }
        _serverConfig.value.saveToSettings(context)
    }
}
