package de.softwareapp_hb.privateqrscanner.i18n

import android.app.LocaleManager
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import android.os.LocaleList
import de.softwareapp_hb.privateqrscanner.R
import java.util.Locale

data class AppLocaleOption(
    val storageTag: String,
    val languageTag: String?,
    val status: AppLocaleStatus,
    val displayName: String
) {
    val isSystemDefault: Boolean = languageTag == null
}

enum class AppLocaleStatus(val storageValue: String) {
    System("system"),
    Complete("complete"),
    Partial("partial"),
    Planned("planned");

    val isSelectable: Boolean
        get() = this == System || this == Complete || this == Partial

    companion object {
        fun fromStorageValue(value: String): AppLocaleStatus {
            return entries.firstOrNull { it.storageValue == value.trim().lowercase(Locale.US) } ?: Planned
        }
    }
}

object AppLocaleCatalog {
    const val SYSTEM_STORAGE_TAG = "system"

    fun load(context: Context): List<AppLocaleOption> {
        val parsed = context.resources.openRawResource(R.raw.supported_locales)
            .bufferedReader(Charsets.UTF_8)
            .use { parse(it.readText()) }
            .filter { it.status.isSelectable }

        val withSystemDefault = if (parsed.any { it.isSystemDefault }) {
            parsed
        } else {
            listOf(systemDefaultOption()) + parsed
        }

        return withSystemDefault.ifEmpty {
            listOf(systemDefaultOption(), AppLocaleOption("en", "en", AppLocaleStatus.Complete, "English"))
        }
    }

    fun parse(rawCatalog: String): List<AppLocaleOption> {
        return rawCatalog
            .lineSequence()
            .map { it.trim() }
            .filter { it.isNotEmpty() && !it.startsWith("#") }
            .mapNotNull(::parseLine)
            .toList()
    }

    fun resolveOption(options: List<AppLocaleOption>, storageTag: String?): AppLocaleOption {
        return options.firstOrNull { it.storageTag == storageTag } ?: options.firstOrNull { it.isSystemDefault }
            ?: systemDefaultOption()
    }

    fun resolveLanguageTag(options: List<AppLocaleOption>, storageTag: String?): String? {
        return resolveOption(options, storageTag).languageTag
    }

    private fun parseLine(line: String): AppLocaleOption? {
        val columns = line.split('\t')
        if (columns.size < 4 || columns[0] == "tag") return null

        val tag = columns[0].trim()
        val status = AppLocaleStatus.fromStorageValue(columns[2])
        val displayName = columns[3].trim().ifBlank { tag }
        val languageTag = tag.takeUnless { it == SYSTEM_STORAGE_TAG || status == AppLocaleStatus.System }

        return AppLocaleOption(
            storageTag = tag,
            languageTag = languageTag,
            status = status,
            displayName = displayName
        )
    }

    private fun systemDefaultOption(): AppLocaleOption {
        return AppLocaleOption(
            storageTag = SYSTEM_STORAGE_TAG,
            languageTag = null,
            status = AppLocaleStatus.System,
            displayName = "System default"
        )
    }
}

object AppLocaleApplier {
    fun applyFrameworkAppLocale(context: Context, languageTag: String?) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return

        val localeManager = context.getSystemService(LocaleManager::class.java)
        val targetLocales = if (languageTag.isNullOrBlank()) {
            LocaleList.getEmptyLocaleList()
        } else {
            LocaleList.forLanguageTags(languageTag)
        }

        if (localeManager.applicationLocales.toLanguageTags() != targetLocales.toLanguageTags()) {
            localeManager.applicationLocales = targetLocales
        }
    }

    fun wrapContext(baseContext: Context, languageTag: String?): Context {
        val baseConfiguration = baseContext.resources.configuration
        val locale = if (languageTag.isNullOrBlank()) {
            baseConfiguration.locales.get(0) ?: Locale.getDefault()
        } else {
            Locale.forLanguageTag(languageTag)
        }

        Locale.setDefault(locale)
        LocaleList.setDefault(LocaleList(locale))

        if (languageTag.isNullOrBlank()) return baseContext

        val localizedConfiguration = Configuration(baseConfiguration).apply {
            val locales = LocaleList(locale)
            setLocales(locales)
            setLayoutDirection(locale)
        }

        return baseContext.createConfigurationContext(localizedConfiguration)
    }
}
