package com.github.nullptroma.wallenc.app.locale

import android.content.Context
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.github.nullptroma.wallenc.ui.locale.AppLanguage
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import androidx.core.content.edit

/**
 * Хранение выбранного языка. SharedPreferences — для синхронного чтения в
 * [android.app.Application.attachBaseContext]; DataStore — только для миграции со старой версии.
 */
internal object AppLocaleStorage {

    private const val PREFS_NAME = "app_locale"
    private const val PREFS_KEY_LANGUAGE = "app_language"

    private val legacyLanguageKey = stringPreferencesKey(PREFS_KEY_LANGUAGE)

    private val Context.legacyLocaleDataStore: DataStore<Preferences> by preferencesDataStore(
        name = "app_locale",
    )

    /** В [android.app.Application.attachBaseContext] [Context.getApplicationContext] ещё null. */
    private fun storageContext(context: Context): Context =
        context.applicationContext ?: context

    private fun prefs(context: Context) =
        storageContext(context).getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)

    fun languageFlow(context: Context): Flow<AppLanguage> = callbackFlow {
        val storage = storageContext(context)
        val listener =
            android.content.SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
                if (key == PREFS_KEY_LANGUAGE) {
                    trySend(readLanguageSync(storage))
                }
            }
        trySend(readLanguageSync(storage))
        prefs(storage).registerOnSharedPreferenceChangeListener(listener)
        awaitClose { prefs(storage).unregisterOnSharedPreferenceChangeListener(listener) }
    }

    fun persistLanguage(context: Context, language: AppLanguage) {
        prefs(context).edit { putString(PREFS_KEY_LANGUAGE, language.storageValue) }
        applyLanguage(language)
    }

    fun readLanguageSync(context: Context): AppLanguage {
        val raw = prefs(context).getString(PREFS_KEY_LANGUAGE, null)
        return raw?.toAppLanguage() ?: AppLanguage.System
    }

    /** Без блокировок; безопасно из [android.app.Application.attachBaseContext]. */
    fun applyStored(context: Context) {
        applyLanguage(readLanguageSync(context))
    }

    fun applyLanguage(language: AppLanguage) {
        val localeList = when (language) {
            AppLanguage.System -> LocaleListCompat.getEmptyLocaleList()
            AppLanguage.English -> LocaleListCompat.forLanguageTags("en")
            AppLanguage.Russian -> LocaleListCompat.forLanguageTags("ru")
        }
        AppCompatDelegate.setApplicationLocales(localeList)
    }

    /** Однократно переносит значение из DataStore (если SP ещё пуст). */
    fun migrateLegacyDataStoreIfNeeded(context: Context) {
        val storage = storageContext(context)
        if (prefs(storage).contains(PREFS_KEY_LANGUAGE)) {
            return
        }
        runBlocking {
            runCatching {
                val legacy = storage.legacyLocaleDataStore.data.first()[legacyLanguageKey]
                if (legacy != null) {
                    prefs(storage).edit { putString(PREFS_KEY_LANGUAGE, legacy) }
                }
            }
        }
    }

    private fun String.toAppLanguage(): AppLanguage = when (this) {
        STORAGE_EN -> AppLanguage.English
        STORAGE_RU -> AppLanguage.Russian
        else -> AppLanguage.System
    }

    private val AppLanguage.storageValue: String
        get() = when (this) {
            AppLanguage.System -> STORAGE_SYSTEM
            AppLanguage.English -> STORAGE_EN
            AppLanguage.Russian -> STORAGE_RU
        }

    private const val STORAGE_SYSTEM = "system"
    private const val STORAGE_EN = "en"
    private const val STORAGE_RU = "ru"
}
