@file:Suppress("HardCodedStringLiteral")

package eu.gaudian.translator.viewmodel

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import eu.gaudian.translator.model.LanguageModel
import eu.gaudian.translator.model.communication.ApiManager
import eu.gaudian.translator.model.communication.ApiProvider
import eu.gaudian.translator.model.repository.ApiRepository
import eu.gaudian.translator.model.repository.SettingsRepository
import eu.gaudian.translator.utils.Log
import kotlinx.coroutines.Dispatchers
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.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject

data class ProviderState(
    val provider: ApiProvider,
    val apiKey: String = "",
    val hasKey: Boolean = false,
    val validationMessage: String = "",
    val isEditing: Boolean = false
)

data class ApiKeyManagementState(
    val providerStates: List<ProviderState> = emptyList(),
    val isAddingProvider: Boolean = false,
    val providerKeyForAddingModel: String? = null,
    val isAddingModel: Boolean = false,
    val addModelError: String? = null,
    val isScanningModels: Boolean = false,
    val scannedModels: List<LanguageModel> = emptyList(),
    val providerKeyForEditing: String? = null,
    val modelIdForEditing: String? = null,
    val showWipeAllConfirm: Boolean = false
)

@HiltViewModel
class ApiViewModel @Inject constructor(
    application: Application,
    private val settingsRepository: SettingsRepository,
    private val apiManager: ApiManager,
    private val apiRepository: ApiRepository,
) : AndroidViewModel(application) {

    @Suppress("PrivatePropertyName")
    private val TAG = "ApiViewModel"
    private val _apiKeyManagementState = MutableStateFlow(ApiKeyManagementState())
    val apiKeyManagementState: StateFlow<ApiKeyManagementState> = _apiKeyManagementState.asStateFlow()
    val interval:Long = 1000

    // 1. Source of Truth: All providers from DB
    val allProviders: StateFlow<List<ApiProvider>> = apiRepository.getProviders()
        .onEach { Log.d(TAG, "allProviders updated: ${it.size} items") }
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(interval), emptyList())

    // 2. Derived: Only providers that are active (Valid Key or Custom)
    val allValidProviders: StateFlow<List<ApiProvider>> = allProviders
        .map { list -> list.filter { it.hasValidKey || it.isCustom } }
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(interval), emptyList())

    // 3. Derived: All models
    val allModels: StateFlow<List<LanguageModel>> = allProviders
        .map { providers -> providers.flatMap { it.models } }
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(interval), emptyList())

    // 4. Derived: Valid Models
    // Since we now manually remove models when keys are missing, we can trust allModels.
    // However, keeping the filter is safer for edge cases.
    val allValidModels: StateFlow<List<LanguageModel>> = allProviders
        .map { providers ->
            providers
                .filter { it.hasValidKey || it.isCustom }
                .flatMap { it.models }
        }
        .onEach { Log.d(TAG, "allValidModels updated: ${it.size} models available") }
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(interval), emptyList())

    val translationModel: StateFlow<LanguageModel?> = apiRepository.getTranslationModel()
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(interval), null)

    val exerciseModel: StateFlow<LanguageModel?> = apiRepository.getExerciseModel()
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(interval), null)

    val vocabularyModel: StateFlow<LanguageModel?> = apiRepository.getVocabularyModel()
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(interval), null)

    val dictionaryModel: StateFlow<LanguageModel?> = apiRepository.getDictionaryModel()
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(interval), null)

    init {
        // Sync UI state
        viewModelScope.launch {
            apiRepository.getProviders()
                .combine(settingsRepository.getAllApiKeys()) { providers, apiKeys ->
                    providers.map { provider ->
                        val apiKey = apiKeys[provider.key] ?: ""
                        val hasKey = apiKey.isNotBlank()

                        val currentUiState = _apiKeyManagementState.value.providerStates
                            .find { it.provider.key == provider.key }

                        ProviderState(
                            provider = provider,
                            apiKey = currentUiState?.apiKey.takeIf { !it.isNullOrBlank() && currentUiState?.isEditing == true } ?: apiKey,
                            hasKey = hasKey,
                            isEditing = currentUiState?.isEditing ?: false,
                            validationMessage = currentUiState?.validationMessage ?: ""
                        )
                    }
                }
                .collect { providerStates ->
                    _apiKeyManagementState.update { it.copy(providerStates = providerStates) }
                }
        }
    }

    /* --- Provider Management --- */

    fun startAddingProvider() = _apiKeyManagementState.update { it.copy(isAddingProvider = true) }

    fun cancelAddProvider() = _apiKeyManagementState.update { it.copy(isAddingProvider = false) }

    fun addProvider(provider: ApiProvider) = viewModelScope.launch {
        Log.i(TAG, "Adding custom provider: ${provider.displayName}")
        // Logic to ensure unique keys
        val existingKeys = allProviders.value.map { it.key }.toSet()
        fun slugify(input: String): String {
            val base = input.lowercase().trim()
                .replace("[^a-z0-9]+".toRegex(), "-")
                .trim('-')
            return when {
                base.isBlank() -> "custom"
                base.length <= 40 -> base
                else -> base.take(40).trim('-')
            }
        }
        var desired = provider.key.trim()
        desired = if (desired.isBlank()) slugify(provider.displayName) else slugify(desired)
        var unique = desired
        var counter = 1
        while (unique.isBlank() || existingKeys.contains(unique)) {
            unique = if (desired.isBlank()) "custom-$counter" else "$desired-$counter"
            counter++
        }

        val safeProvider = provider.copy(key = unique, isCustom = true)
        apiRepository.addProvider(safeProvider)
        cancelAddProvider()
    }

    fun deleteProvider(providerKey: String) = viewModelScope.launch {
        Log.i(TAG, "Deleting provider: $providerKey")
        apiRepository.deleteProvider(providerKey)
        settingsRepository.deleteApiKey(providerKey)
    }

    fun updateApiKeyForProvider(providerKey: String, newKey: String) {
        _apiKeyManagementState.update { currentState ->
            val updatedStates = currentState.providerStates.map {
                if (it.provider.key == providerKey) it.copy(apiKey = newKey, validationMessage = "") else it
            }
            currentState.copy(providerStates = updatedStates)
        }
    }

    fun deleteApiKeyForProvider(providerKey: String) = viewModelScope.launch {
        Log.i(TAG, "Deleting API key for: $providerKey")
        settingsRepository.deleteApiKey(providerKey)

        // MANUAL LOGIC: Key deleted -> Remove pre-configured models
        apiRepository.removeDefaultModels(providerKey)

        // Refresh fallback selections
        apiRepository.initialInit()
    }

    fun saveApiKeyForProvider(providerKey: String) {
        val providerState = _apiKeyManagementState.value.providerStates.find { it.provider.key == providerKey } ?: return
        val keyToSave = providerState.apiKey.filter { !it.isWhitespace() }
        val provider = providerState.provider

        viewModelScope.launch(Dispatchers.IO) {
            val (isValid, message) = apiManager.validateApiKey(keyToSave, provider)
            withContext(Dispatchers.Main) {
                if (isValid) {
                    settingsRepository.saveApiKey(provider, keyToSave)

                    // MANUAL LOGIC: Key activated -> Add pre-configured models
                    Log.i(TAG, "Key valid. Adding default models for $providerKey")
                    apiRepository.addDefaultModels(providerKey)

                    toggleEditModeForProvider(providerKey, false)
                    apiRepository.initialInit() // Re-check fallbacks
                } else {
                    val errorMessage = "Invalid key. $message"
                    _apiKeyManagementState.update { currentState ->
                        val updatedStates = currentState.providerStates.map {
                            if (it.provider.key == providerKey) it.copy(validationMessage = errorMessage) else it
                        }
                        currentState.copy(providerStates = updatedStates)
                    }
                }
            }
        }
    }

    fun toggleEditModeForProvider(providerKey: String, isEditing: Boolean? = null) {
        _apiKeyManagementState.update { currentState ->
            val updatedStates = currentState.providerStates.map {
                if (it.provider.key == providerKey) {
                    it.copy(isEditing = isEditing ?: !it.isEditing, validationMessage = "")
                } else {
                    it
                }
            }
            currentState.copy(providerStates = updatedStates)
        }
    }

    /* --- Provider Editing (Name/Url) --- */

    fun startEditingProvider(providerKey: String) = _apiKeyManagementState.update {
        it.copy(providerKeyForEditing = providerKey, modelIdForEditing = null)
    }

    fun applyEditProvider(updated: ApiProvider) = viewModelScope.launch {
        apiRepository.updateProvider(updated)
        cancelEditing()
    }

    /* --- Model Management --- */

    fun startAddingModelForProvider(providerKey: String) = _apiKeyManagementState.update {
        it.copy(providerKeyForAddingModel = providerKey, scannedModels = emptyList(), addModelError = null)
    }

    fun cancelAddModel() = _apiKeyManagementState.update {
        it.copy(providerKeyForAddingModel = null, isAddingModel = false, addModelError = null, isScanningModels = false, scannedModels = emptyList())
    }

    fun startEditingModel(providerKey: String, modelId: String) = _apiKeyManagementState.update {
        it.copy(providerKeyForEditing = providerKey, modelIdForEditing = modelId, addModelError = null)
    }

    fun cancelEditing() = _apiKeyManagementState.update {
        it.copy(providerKeyForEditing = null, modelIdForEditing = null)
    }

    fun applyEditModel(providerKey: String, updatedModel: LanguageModel) = viewModelScope.launch {
        val state = _apiKeyManagementState.value
        val provider = state.providerStates.find { it.provider.key == providerKey }?.provider ?: return@launch
        val originalId = state.modelIdForEditing ?: return@launch

        val duplicateExists = provider.models.any { it.modelId == updatedModel.modelId && it.modelId != originalId }
        if (duplicateExists) {
            _apiKeyManagementState.update { it.copy(addModelError = "A model with this ID already exists.") }
            return@launch
        }

        val newModels = provider.models.map { m ->
            if (m.modelId == originalId && m.isCustom) updatedModel.copy(isCustom = true) else m
        }
        val updatedProvider = provider.copy(models = newModels)
        apiRepository.updateProvider(updatedProvider)
        cancelEditing()
    }

    fun scanModelsForProvider(providerKey: String) = viewModelScope.launch {
        val state = _apiKeyManagementState.value
        val providerState = state.providerStates.find { it.provider.key == providerKey } ?: return@launch

        _apiKeyManagementState.update { it.copy(isScanningModels = true, addModelError = null, scannedModels = emptyList()) }

        val apiKey = settingsRepository.getAllApiKeys().first()[providerKey] ?: providerState.apiKey
        val (models, error) = apiManager.fetchAvailableModels(apiKey, providerState.provider)

        _apiKeyManagementState.update {
            it.copy(
                isScanningModels = false,
                addModelError = error,
                scannedModels = models
            )
        }
    }

    fun addModelToProvider(providerKey: String, model: LanguageModel) = viewModelScope.launch {
        _apiKeyManagementState.update { it.copy(isAddingModel = true, addModelError = null) }
        val providerState = _apiKeyManagementState.value.providerStates.find { it.provider.key == providerKey }

        if (providerState == null) {
            _apiKeyManagementState.update { it.copy(isAddingModel = false, addModelError = "Provider not found.") }
            return@launch
        }

        val apiKeyForValidation = settingsRepository.getAllApiKeys().first()[providerKey]
            ?: providerState.apiKey.takeIf { it.isNotBlank() }
        val base = providerState.provider.baseUrl.trim().lowercase()
        val isLocalHost = (base.contains("localhost") || base.contains("127.0.0.1") || base.startsWith("10."))

        var canAddDirectly = isLocalHost || apiKeyForValidation == null
        var validationMessage: String? = null

        if (!canAddDirectly) {
            val (isValid, message) = apiManager.validateModel(apiKeyForValidation!!, providerState.provider, model)
            validationMessage = message
            canAddDirectly = isValid
        }

        if (canAddDirectly) {
            val provider = providerState.provider
            // Mark manually added models as custom
            val filteredModels = provider.models.filterNot { it.modelId == model.modelId }.toMutableList()
            filteredModels.add(model.copy(isCustom = true))

            val updatedProvider = provider.copy(models = filteredModels)
            apiRepository.updateProvider(updatedProvider)
            cancelAddModel()
        } else {
            _apiKeyManagementState.update {
                it.copy(isAddingModel = false, addModelError = "Model validation failed: ${validationMessage ?: "Unknown error"}")
            }
        }
    }

    fun deleteModelFromProvider(providerKey: String, modelId: String) = viewModelScope.launch {
        val provider = _apiKeyManagementState.value.providerStates.find { it.provider.key == providerKey }?.provider
        if (provider != null) {
            val updatedModels = provider.models.filterNot { it.modelId == modelId && it.isCustom }
            if (updatedModels.size < provider.models.size) {
                val updatedProvider = provider.copy(models = updatedModels)
                apiRepository.updateProvider(updatedProvider)
            } else {
                Log.w(TAG, "Attempted to delete a non-custom model or model not found.")
            }
        }
    }
    fun showWipeAllConfirm() = _apiKeyManagementState.update { it.copy(showWipeAllConfirm = true) }
    fun hideWipeAllConfirm() = _apiKeyManagementState.update { it.copy(showWipeAllConfirm = false) }

    fun confirmWipeAll() = viewModelScope.launch {
        try {
            apiRepository.wipeAll()
        } finally {
            _apiKeyManagementState.update { it.copy(showWipeAllConfirm = false) }
        }
    }

    fun checkProviderAvailability(baseUrl: String, callback: (Boolean, String) -> Unit) {
        viewModelScope.launch(Dispatchers.IO) {
            val (ok, message) = apiManager.checkProviderAvailability(baseUrl)
            withContext(Dispatchers.Main) { callback(ok, message) }
        }
    }

    fun setTranslationModel(model: LanguageModel?) = viewModelScope.launch { model?.let { apiRepository.setTranslationModel(it) } }
    fun setExerciseModel(model: LanguageModel?) = viewModelScope.launch { model?.let { apiRepository.setExerciseModel(it) } }
    fun setVocabularyModel(model: LanguageModel?) = viewModelScope.launch { model?.let { apiRepository.setVocabularyModel(it) } }
    fun setDictionaryModel(model: LanguageModel?) = viewModelScope.launch { model?.let { apiRepository.setDictionaryModel(it) } }
}