@file:Suppress("HardCodedStringLiteral")

package eu.gaudian.translator.model.repository

import android.content.Context
import android.content.pm.PackageManager
import eu.gaudian.translator.R
import eu.gaudian.translator.model.Language
import eu.gaudian.translator.model.parseLanguagesFromResources
import eu.gaudian.translator.model.repository.DataStoreKeys.LANGUAGE_INIT_METADATA_KEY
import eu.gaudian.translator.utils.Log
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull

enum class LanguageListType {
    DEFAULT,
    CUSTOM,
    ALL,
    FAVORITE,
    HISTORY
}

class LanguageRepository(private val context: Context) {

    private val dataStore = context.dataStore

    private fun getKeyForType(type: LanguageListType) = when (type) {
        // Note: For ALL we will now store only the enabled language IDs (Int) instead of full Language objects
        LanguageListType.ALL -> DataStoreKeys.ALL_LANGUAGES_KEY
        LanguageListType.DEFAULT -> DataStoreKeys.DEFAULT_LANGUAGES_KEY
        LanguageListType.CUSTOM -> DataStoreKeys.CUSTOM_LANGUAGES_KEY
        LanguageListType.FAVORITE -> DataStoreKeys.FAVORITE_LANGUAGES_KEY
        LanguageListType.HISTORY -> DataStoreKeys.LANGUAGE_HISTORY_KEY
    }

    // Returns a flow of the master catalog = DEFAULT + CUSTOM with conflict disambiguation applied
    private fun masterLanguagesFlow(): Flow<List<Language>> {
        return kotlinx.coroutines.flow.combine(
            loadLanguages(LanguageListType.DEFAULT),
            loadLanguages(LanguageListType.CUSTOM)
        ) { defaults, customs ->
            val master = (defaults + customs)
            disambiguateConflictingNames(master)
        }
    }

    // Suffix region codes for languages with duplicate names within the provided list
    private fun disambiguateConflictingNames(list: List<Language>): List<Language> {
        if (list.isEmpty()) return list
        // Define a regex that matches a trailing region suffix we add, e.g. " (DE)" or " (PT)"
        val suffixRegex = " \\([A-Z]{2,}\\)$".toRegex()

        // Compute base names by stripping any existing suffix first
        val baseNames = list.associate { lang ->
            lang.name to lang.name.replace(suffixRegex, "")
        }
        // Count occurrences by base name
        val countsByBase = list
            .map { baseNames[it.name] ?: it.name }
            .groupingBy { it }
            .eachCount()

        // Map each language to a display name based on base name conflict
        return list.map { lang ->
            val base = baseNames[lang.name] ?: lang.name
            val count = countsByBase[base] ?: 0
            if (count > 1 && lang.region.isNotEmpty()) {
                val suffix = lang.region.uppercase()
                // Always construct from base to prevent duplicate/chained suffixes
                lang.copy(name = "$base ($suffix)")
            } else {
                // No conflict: ensure we show the clean base
                lang.copy(name = base)
            }
        }
    }

    @Suppress("unused")
    suspend fun wipeHistoryAndFavorites() {
        clearLanguages(LanguageListType.HISTORY)
        clearLanguages(LanguageListType.FAVORITE)
        saveSelectedSourceLanguage(null)
        saveSelectedTargetLanguage(null)
    }

    suspend fun initializeDefaultLanguages() {
        Log.d("LanguageRepository", "Initializing default languages")
        try {
            // Check if we already have default languages saved
            val savedDefaultLanguages = loadLanguages(LanguageListType.DEFAULT).firstOrNull() ?: emptyList()
            
            // Check if we need to reparse languages (first run, version change, or language change)
            val shouldReparse = shouldReparseLanguages(savedDefaultLanguages)
            
            if (shouldReparse) {
                Log.d("LanguageRepository", "Parsing languages from resources")
                val parsedLanguages = parseLanguagesFromResources(context)
                saveLanguages(LanguageListType.DEFAULT, parsedLanguages)
                // Save the current app version and locale to detect changes next time
                saveLanguageInitializationMetadata()
            } else {
                Log.d("LanguageRepository", "Using cached default languages")
            }
        } catch (e: Exception) {
            Log.e("LanguageRepository", "Error initializing default languages: ${e.message}", e)
        }
    }

    private suspend fun shouldReparseLanguages(savedLanguages: List<Language>): Boolean {
        // Always reparse if no languages are saved
        if (savedLanguages.isEmpty()) {
            return true
        }
        
        // Get expected language count from resources dynamically
        val expectedCount = context.resources.getStringArray(R.array.language_codes).size
        
        // Check if the number of languages matches expected count from resources
        if (savedLanguages.size != expectedCount) {
            return true
        }
        
        // Check if app version has changed (indicating possible new languages)
        val metadata = getLanguageInitializationMetadata()
        val currentVersion = try {
            context.packageManager.getPackageInfo(context.packageName, 0).versionName
        } catch (_: PackageManager.NameNotFoundException) {
            // If we can't get the package info, assume version changed to trigger reparse
            return true
        }
        if (metadata?.appVersion != currentVersion) {
            return true
        }
        
        // Check if system locale has changed (affecting localized language names)
        val currentLocale = context.resources.configuration.locales.get(0)?.toLanguageTag()
        return metadata?.systemLocale != currentLocale
    }

    private suspend fun saveLanguageInitializationMetadata() {
        val currentVersion = try {
            context.packageManager.getPackageInfo(context.packageName, 0).versionName
        } catch (_: PackageManager.NameNotFoundException) {
            // If we can't get the package info, use a default value
            "unknown"
        }
        val currentLocale = context.resources.configuration.locales.get(0)?.toLanguageTag() ?: ""
        
        val metadata = LanguageInitializationMetadata(
            appVersion = currentVersion,
            systemLocale = currentLocale,
            timestamp = System.currentTimeMillis()
        )
        
        dataStore.saveObject(LANGUAGE_INIT_METADATA_KEY, metadata)
    }

    private suspend fun getLanguageInitializationMetadata(): LanguageInitializationMetadata? {
        return try {
            dataStore.loadObject<LanguageInitializationMetadata>(LANGUAGE_INIT_METADATA_KEY).firstOrNull()
        } catch (_: Exception) {
            null
        }
    }

    suspend fun initializeAllLanguages() {
        Log.d("LanguageRepository", "Initializing enabled languages (ALL as IDs)")
        try {
            val defaultLanguages = loadLanguages(LanguageListType.DEFAULT).first()
            val customLanguages = loadLanguages(LanguageListType.CUSTOM).first()
            val master = (defaultLanguages + customLanguages).distinctBy { it.nameResId }

            // Get existing enabled IDs
            val existingEnabled: List<Int> = try {
                context.dataStore.loadObjectList<Int>(DataStoreKeys.ALL_LANGUAGES_KEY).firstOrNull() ?: emptyList()
            } catch (_: Exception) {
                emptyList()
            }
            val masterIds = master.map { it.nameResId }.toSet()
            
            // Sanitize existing enabled IDs (remove any that are no longer valid)
            val existingValidIds = existingEnabled.filter { it in masterIds }.toSet()
            
            // Add any new languages from master that aren't already enabled
            val newLanguageIds = masterIds - existingValidIds
            
            // Combine: keep existing enabled + add new languages
            val finalIds = (existingValidIds + newLanguageIds).toList()

            context.dataStore.saveObjectList(DataStoreKeys.ALL_LANGUAGES_KEY, finalIds)

            val historyLanguages = loadLanguages(LanguageListType.HISTORY).firstOrNull() ?: emptyList()
            if (historyLanguages.size > 5) {
                saveLanguages(LanguageListType.HISTORY, historyLanguages.takeLast(5))
            }
        } catch (e: Exception) {
            Log.e("LanguageRepository", "Error initializing enabled languages: ${e.message}", e)
        }
    }

    suspend fun getLanguagesByResourceIds(ids: Set<Int>): List<Language> {
        val master = masterLanguagesFlow().first()
        return master.filter { it.nameResId in ids }
    }

    fun loadMasterLanguages(): Flow<List<Language>> = masterLanguagesFlow()

    suspend fun setEnabledLanguagesByIds(ids: List<Int>) {
        dataStore.saveObjectList(DataStoreKeys.ALL_LANGUAGES_KEY, ids)
    }

    suspend fun editCustomLanguage(languageId: Int, newName: String?, newCode: String, newRegion: String) {
        // Update in DEFAULT or CUSTOM by id (nameResId)
        val defaults = loadLanguages(LanguageListType.DEFAULT).first().toMutableList()
        val customs = loadLanguages(LanguageListType.CUSTOM).first().toMutableList()
        var updated = false
        for (i in defaults.indices) {
            if (defaults[i].nameResId == languageId) {
                val l = defaults[i]
                // Default languages: do not change name
                defaults[i] = l.copy(code = newCode, region = newRegion)
                updated = true
                break
            }
        }
        if (!updated) {
            for (i in customs.indices) {
                if (customs[i].nameResId == languageId) {
                    val l = customs[i]
                    // Custom languages: allow name editing if provided
                    val targetName = newName ?: l.name
                    // Prevent exact duplicates by name+region+code with other customs
                    val duplicate = customs.withIndex().any { (idx, other) ->
                        idx != i && other.name.equals(targetName, true) && other.region.equals(newRegion, true) && other.code.equals(newCode, true)
                    }
                    if (!duplicate) {
                        customs[i] = l.copy(name = targetName, code = newCode, region = newRegion)
                        updated = true
                    } else {
                        Log.w("LanguageRepository", "Skipping update to avoid duplicate custom language")
                    }
                    break
                }
            }
        }
        if (updated) {
            saveLanguages(LanguageListType.DEFAULT, defaults)
            saveLanguages(LanguageListType.CUSTOM, customs)
            // Update selected languages if necessary
            val src = loadSelectedSourceLanguage().first()
            if (src?.nameResId == languageId) saveSelectedSourceLanguage(src.copy(name = newName ?: src.name, code = newCode, region = newRegion))
            val tgt = loadSelectedTargetLanguage().first()
            if (tgt?.nameResId == languageId) saveSelectedTargetLanguage(tgt.copy(name = newName ?: tgt.name, code = newCode, region = newRegion))
            val dict = loadSelectedDictionaryLanguage().first()
            if (dict?.nameResId == languageId) saveSelectedDictionaryLanguage(dict.copy(name = newName ?: dict.name, code = newCode, region = newRegion))
            initializeAllLanguages()
        }
    }

    fun loadLanguages(type: LanguageListType): Flow<List<Language>> {
        return when (type) {
            LanguageListType.ALL -> {
                // Enabled languages (IDs) mapped to actual Language objects from master catalog
                kotlinx.coroutines.flow.combine(
                    dataStore.loadObjectList<Int>(getKeyForType(type)),
                    masterLanguagesFlow()
                ) { ids, master ->
                    val idSet = ids.toSet()
                    disambiguateConflictingNames(master.filter { it.nameResId in idSet })
                }
            }
            LanguageListType.FAVORITE, LanguageListType.HISTORY -> {
                // Internally store only the language keys (nameResId) to avoid duplicate Language instances
                kotlinx.coroutines.flow.combine(
                    dataStore.loadObjectList<Int>(getKeyForType(type)),
                    masterLanguagesFlow()
                ) { ids, master ->
                    val idSet = ids.toSet()
                    master.filter { it.nameResId in idSet }
                }
            }
            else -> dataStore.loadObjectList(getKeyForType(type))
        }
    }

    suspend fun saveLanguages(type: LanguageListType, languages: List<Language>) {
        when (type) {
            LanguageListType.ALL, LanguageListType.FAVORITE, LanguageListType.HISTORY -> {
                val ids = languages.map { it.nameResId }
                dataStore.saveObjectList(getKeyForType(type), ids)
            }
            else -> dataStore.saveObjectList(getKeyForType(type), languages)
        }
    }

    suspend fun clearLanguages(type: LanguageListType) {
        dataStore.clear(getKeyForType(type))
    }

    fun loadSelectedSourceLanguage(): Flow<Language?> {
        return dataStore.loadObject(DataStoreKeys.SELECTED_SOURCE_LANGUAGE_KEY)
    }

    suspend fun saveSelectedSourceLanguage(language: Language?) {
        dataStore.saveObject(DataStoreKeys.SELECTED_SOURCE_LANGUAGE_KEY, language)
    }

    fun loadSelectedTargetLanguage(): Flow<Language?> {
        return dataStore.loadObject(DataStoreKeys.SELECTED_TARGET_LANGUAGE_KEY)
    }

    suspend fun saveSelectedTargetLanguage(language: Language?) {
        dataStore.saveObject(DataStoreKeys.SELECTED_TARGET_LANGUAGE_KEY, language)
    }

    fun loadSelectedDictionaryLanguage(): Flow<Language?> {
        return dataStore.loadObject(DataStoreKeys.SELECTED_DICTIONARY_LANGUAGE_KEY)
    }

    suspend fun saveSelectedDictionaryLanguage(language: Language?) {
        dataStore.saveObject(DataStoreKeys.SELECTED_DICTIONARY_LANGUAGE_KEY, language)
    }

    suspend fun addCustomLanguage(language: Language) {
        val customLanguages = loadLanguages(LanguageListType.CUSTOM).first().toMutableList()

        val newId = -(System.currentTimeMillis().toInt())

        val newLanguage = language.copy(nameResId = newId, isCustom = true)

        // Allow same names with different regions; prevent exact duplicates by name+region+code
        if (!customLanguages.any { it.name.equals(newLanguage.name, true) && it.region.equals(newLanguage.region, true) && it.code.equals(newLanguage.code, true) }) {
            customLanguages.add(newLanguage)
            saveLanguages(LanguageListType.CUSTOM, customLanguages)
            initializeAllLanguages()
        }
    }

    suspend fun deleteCustomLanguage(language: Language) {
        // Read all lists needed first
        val customLanguages = loadLanguages(LanguageListType.CUSTOM).first().toMutableList()
        val historyLanguages = loadLanguages(LanguageListType.HISTORY).first().toMutableList()
        val favoriteLanguages = loadLanguages(LanguageListType.FAVORITE).first().toMutableList()
        val enabledIds = context.dataStore.loadObjectList<Int>(DataStoreKeys.ALL_LANGUAGES_KEY).firstOrNull()?.toMutableList() ?: mutableListOf()

        // Perform removals
        val wasCustomRemoved = customLanguages.removeIf { it.nameResId == language.nameResId }
        val wasHistoryRemoved = historyLanguages.removeIf { it.nameResId == language.nameResId }
        val wasFavoriteRemoved = favoriteLanguages.removeIf { it.nameResId == language.nameResId }
        val wasEnabledRemoved = enabledIds.removeIf { it == language.nameResId }

        // Write back to DataStore only if changes were made
        if (wasCustomRemoved) {
            saveLanguages(LanguageListType.CUSTOM, customLanguages)
            // Re-run initialization to update the enabled list
            initializeAllLanguages()
        }
        if (wasHistoryRemoved) {
            saveLanguages(LanguageListType.HISTORY, historyLanguages)
        }
        if (wasFavoriteRemoved) {
            saveLanguages(LanguageListType.FAVORITE, favoriteLanguages)
        }
        if (wasEnabledRemoved) {
            context.dataStore.saveObjectList(DataStoreKeys.ALL_LANGUAGES_KEY, enabledIds)
        }
    }

    suspend fun getLanguageById(id: Int): Language? {
        if (id == 0) return null
        return try {
            masterLanguagesFlow().first().find { it.nameResId == id }
        } catch (_: Exception) {
            null
        }
    }
}
