@file:Suppress("HardCodedStringLiteral")

package eu.gaudian.translator.viewmodel

import android.app.Application
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.core.stringSetPreferencesKey
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.application
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import eu.gaudian.translator.R
import eu.gaudian.translator.model.WidgetType
import eu.gaudian.translator.model.repository.ApiLogRepository
import eu.gaudian.translator.model.repository.SettingsRepository
import eu.gaudian.translator.model.repository.dataStore
import eu.gaudian.translator.utils.Log
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject


@HiltViewModel
class SettingsViewModel @Inject constructor(
    application: Application,
    private val settingsRepository: SettingsRepository,
    private val apiLogRepository: ApiLogRepository
) : AndroidViewModel(application) {
    companion object {
        // Stable, non-localized keys for vocabulary stages
        const val STAGE_NEW: String = "stage_new"
        const val STAGE_1: String = "stage_1"
        const val STAGE_2: String = "stage_2"
        const val STAGE_3: String = "stage_3"
        const val STAGE_4: String = "stage_4"
        const val STAGE_5: String = "stage_5"
        const val STAGE_LEARNED: String = "stage_learned"
    }

    val apiLogs = apiLogRepository.getLogs()
    fun clearApiLogs() = viewModelScope.launch { apiLogRepository.clear() }

    private object PrefKeys {
        val WIDGET_ORDER = stringPreferencesKey("widget_order")
        val COLLAPSED_WIDGET_IDS = stringSetPreferencesKey("collapsed_widget_ids")
        val DASHBOARD_SCROLL_POSITION = stringPreferencesKey("dashboard_scroll_position")
        val DASHBOARD_SCROLL_OFFSET = stringPreferencesKey("dashboard_scroll_offset")
    }

    private val _isInitialized = MutableStateFlow(false)
    val isInitialized: StateFlow<Boolean> = _isInitialized.asStateFlow()
    init {
        viewModelScope.launch {
            //TODO do connection check

            _isInitialized.value = true
        }
    }

    val theme: StateFlow<String> = settingsRepository.theme.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "Default")
    val darkModePreference: StateFlow<String> = settingsRepository.darkModePreference.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "System")
    val fontPreference: StateFlow<String> = settingsRepository.fontPreference.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "Default")
    val introCompleted: StateFlow<Boolean> = settingsRepository.introCompleted.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), true)
    val showBottomNavLabels: StateFlow<Boolean> = settingsRepository.showBottomNavLabels.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), true)


    val isDeveloperModeEnabled: StateFlow<Boolean> = settingsRepository.developerMode.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)
    val dictionarySwitches: StateFlow<Set<String>> = settingsRepository.dictionarySwitches.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptySet())
    val customPrompt: StateFlow<String> = settingsRepository.customPromptTranslation.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "")
    val customPromptVocabulary: StateFlow<String> = settingsRepository.customPromptVocabulary.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "")
    val customPromptDictionary: StateFlow<String> = settingsRepository.customPromptDictionary.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "")
    val customPromptExercise: StateFlow<String> = settingsRepository.customPromptExercise.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "")
    val speakingSpeed: StateFlow<Int> = settingsRepository.speakingSpeed.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 100)

    val showHints: StateFlow<Boolean> = settingsRepository.showHints.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), true)
    fun setShowHints(enabled: Boolean) = viewModelScope.launch { settingsRepository.showHints.set(enabled) }

    val experimentalFeatures: StateFlow<Boolean> = settingsRepository.experimentalFeatures.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)
    val connectionConfigured: StateFlow<Boolean> = settingsRepository.connectionConfigured.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), true)
    val tryWiktionaryFirst: StateFlow<Boolean> = settingsRepository.tryWiktionaryFirst.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)

    // LibreTranslate toggle
    val useLibreTranslate: StateFlow<Boolean> = settingsRepository.useLibreTranslate.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)
    fun setUseLibreTranslate(enabled: Boolean) = viewModelScope.launch { settingsRepository.useLibreTranslate.set(enabled) }

    fun setExperimentalFeatures(enabled: Boolean) = viewModelScope.launch { settingsRepository.experimentalFeatures.set(enabled) }
    fun setTryWiktionaryFirst(enabled: Boolean) = viewModelScope.launch { settingsRepository.tryWiktionaryFirst.set(enabled) }

    val widgetOrder: StateFlow<List<WidgetType>> = application.dataStore.data
        .map { preferences ->
            val allWidgets = WidgetType.DEFAULT_ORDER
            val orderString = preferences[PrefKeys.WIDGET_ORDER]

            if (orderString.isNullOrEmpty()) {
                allWidgets
            } else {
                val savedWidgets = orderString.split(",").mapNotNull { WidgetType.fromId(it) }


                if (savedWidgets.size != allWidgets.size) {
                    saveWidgetOrder(allWidgets)
                    allWidgets
                } else {
                    savedWidgets
                }
            }
        }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), WidgetType.DEFAULT_ORDER)


    val collapsedWidgetIds: StateFlow<Set<String>> = application.dataStore.data
        .map { preferences ->
            preferences[PrefKeys.COLLAPSED_WIDGET_IDS] ?: emptySet()
        }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptySet())
    val dailyGoal: StateFlow<Int> = settingsRepository.dailyGoal.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 10)
    val criteriaCorrect: StateFlow<Int> = settingsRepository.criteriaCorrect.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 3)
    val criteriaWrong: StateFlow<Int> = settingsRepository.criteriaWrong.flow
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 2)
    val intervals: StateFlow<Map<String, Int>> = combine(
        settingsRepository.intervalNew.flow,
        settingsRepository.intervalStage1.flow,
        settingsRepository.intervalStage2.flow,
        settingsRepository.intervalStage3.flow,
        settingsRepository.intervalStage4.flow,
        settingsRepository.intervalStage5.flow,
        settingsRepository.intervalLearned.flow
    ) { values ->
        mapOf(
            STAGE_NEW to values[0],
            STAGE_1 to values[1],
            STAGE_2 to values[2],
            STAGE_3 to values[3],
            STAGE_4 to values[4],
            STAGE_5 to values[5],
            STAGE_LEARNED to values[6]
        )
    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyMap())
    fun setInterval(stage: String, days: Int) = viewModelScope.launch {
        when (stage) {
            STAGE_NEW -> settingsRepository.intervalNew.set(days)
            STAGE_1 -> settingsRepository.intervalStage1.set(days)
            STAGE_2 -> settingsRepository.intervalStage2.set(days)
            STAGE_3 -> settingsRepository.intervalStage3.set(days)
            STAGE_4 -> settingsRepository.intervalStage4.set(days)
            STAGE_5 -> settingsRepository.intervalStage5.set(days)
            STAGE_LEARNED -> settingsRepository.intervalLearned.set(days)
        }
    }

    /** Resets all vocabulary stage intervals to their default values. */
    fun resetIntervalsToDefaults() = viewModelScope.launch {
        // Defaults as defined in SettingsRepository
        settingsRepository.intervalNew.set(1)
        settingsRepository.intervalStage1.set(3)
        settingsRepository.intervalStage2.set(7)
        settingsRepository.intervalStage3.set(14)
        settingsRepository.intervalStage4.set(30)
        settingsRepository.intervalStage5.set(60)
        settingsRepository.intervalLearned.set(90)
    }


    fun setTheme(theme: String) = viewModelScope.launch {
        settingsRepository.theme.set(theme)
    }
    fun setDarkModePreference(preference: String) = viewModelScope.launch { settingsRepository.darkModePreference.set(preference) }
    fun setFontPreference(font: String) = viewModelScope.launch { settingsRepository.fontPreference.set(font) }
    fun setShowBottomNavLabels(enabled: Boolean) = viewModelScope.launch { settingsRepository.showBottomNavLabels.set(enabled) }

    fun setDeveloperMode(isEnabled: Boolean) = viewModelScope.launch { settingsRepository.developerMode.set(isEnabled) }
    fun setDailyGoal(goal: Int) = viewModelScope.launch { settingsRepository.dailyGoal.set(goal) }
    fun setDictionarySwitch(switchKey: String, isChecked: Boolean) {
        viewModelScope.launch {
            val currentSwitches = settingsRepository.dictionarySwitches.flow.first().toMutableSet()

            // Normalize legacy values which stored localized labels instead of stable keys
            // by removing the old label for this key (if present) before updating.
            val resources = application.resources
            val keys = resources.getStringArray(R.array.dictionary_content_keys)
            val labels = resources.getStringArray(R.array.dictionary_content)
            val index = keys.indexOf(switchKey)
            if (index >= 0) {
                val legacyLabel = labels[index]
                currentSwitches.remove(legacyLabel)
            }

            if (isChecked) {
                currentSwitches.add(switchKey)
            } else {
                currentSwitches.remove(switchKey)
            }
            settingsRepository.dictionarySwitches.set(currentSwitches)
        }
    }
    fun saveCustomPrompt(customPrompt: String) = viewModelScope.launch { settingsRepository.customPromptTranslation.set(customPrompt) }
    fun saveCustomVocabularyPrompt(customPrompt: String) = viewModelScope.launch { settingsRepository.customPromptVocabulary.set(customPrompt) }
    fun saveCustomPromptDictionary(customPrompt: String) = viewModelScope.launch { settingsRepository.customPromptDictionary.set(customPrompt) }
    fun saveCustomExercisePrompt(customPrompt: String) = viewModelScope.launch { settingsRepository.customPromptExercise.set(customPrompt) }
    fun saveSpeakingSpeed(speed: Int) = viewModelScope.launch { settingsRepository.speakingSpeed.set(speed) }
    fun saveTtsVoiceForLanguage(code: String, region: String, voiceName: String?) = viewModelScope.launch { settingsRepository.setTtsVoiceForLanguage(code, region, voiceName) }
    suspend fun getTtsVoiceForLanguage(code: String, region: String): String? = settingsRepository.getTtsVoiceForLanguage(code, region).first()
    fun setCriteriaCorrect(value: Int) = viewModelScope.launch { settingsRepository.criteriaCorrect.set(value) }
    fun setCriteriaWrong(value: Int) = viewModelScope.launch { settingsRepository.criteriaWrong.set(value) }
    fun saveWidgetOrder(order: List<WidgetType>) = viewModelScope.launch {
        application.dataStore.edit { settings ->
            settings[PrefKeys.WIDGET_ORDER] = order.joinToString(",") { it.id }
        }
        Log.d("SettingsViewModel", "Widget order saved successfully")
    }
    suspend fun setWidgetExpandedState(widgetId: String, isExpanded: Boolean) {
        application.dataStore.edit { settings ->
            val currentCollapsedIds = settings[PrefKeys.COLLAPSED_WIDGET_IDS]?.toMutableSet() ?: mutableSetOf()
            if (isExpanded) {
                currentCollapsedIds.remove(widgetId)
            } else {
                currentCollapsedIds.add(widgetId)
            }
            settings[PrefKeys.COLLAPSED_WIDGET_IDS] = currentCollapsedIds
        }
    }

    // Dashboard scroll state persistence
    val dashboardScrollState: StateFlow<Pair<Int, Int>> = application.dataStore.data
        .map { preferences ->
            val firstVisibleItemIndex = preferences[PrefKeys.DASHBOARD_SCROLL_POSITION]?.toIntOrNull() ?: 0
            val firstVisibleItemScrollOffset = preferences[PrefKeys.DASHBOARD_SCROLL_OFFSET]?.toIntOrNull() ?: 0
            Pair(firstVisibleItemIndex, firstVisibleItemScrollOffset)
        }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), Pair(0, 0))

    fun saveDashboardScrollState(firstVisibleItemIndex: Int, firstVisibleItemScrollOffset: Int) = viewModelScope.launch {
        application.dataStore.edit { settings ->
            settings[PrefKeys.DASHBOARD_SCROLL_POSITION] = firstVisibleItemIndex.toString()
            settings[PrefKeys.DASHBOARD_SCROLL_OFFSET] = firstVisibleItemScrollOffset.toString()
        }
        Log.d("SettingsViewModel", "Dashboard scroll state saved: index=$firstVisibleItemIndex, offset=$firstVisibleItemScrollOffset")
    }
    fun setIntroCompleted(completed: Boolean) = viewModelScope.launch {
        settingsRepository.introCompleted.set(completed)
    }

    /**
     * Checks if the user has seen the "what's new" dialog for the current version
     */
    suspend fun hasSeenCurrentVersion(currentVersion: String): Boolean {
        return settingsRepository.hasSeenCurrentVersion(currentVersion)
    }

    /**
     * Marks the current version as seen by the user
     */
    fun markVersionAsSeen(version: String) = viewModelScope.launch {
        settingsRepository.markVersionAsSeen(version)
    }


}