package com.pixelized.desktop.lwa.ui.composable.character.alterteration

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.tag.TagRepository
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackUio
import com.pixelized.desktop.lwa.ui.composable.textfield.createLwaTextField
import com.pixelized.desktop.lwa.ui.composable.textfield.createLwaTextFieldFlow
import com.pixelized.desktop.lwa.ui.screen.admin.common.tag.GMTagFactory
import com.pixelized.desktop.lwa.ui.screen.admin.common.tag.GMTagUio
import com.pixelized.desktop.lwa.utils.extention.unAccent
import com.pixelized.shared.lwa.model.alteration.Alteration
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runBlocking
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.game_master__alteration__filter
import org.jetbrains.compose.resources.getString

class CharacterSheetAlterationDialogViewModel(
    private val characterSheetRepository: CharacterSheetRepository,
    alterationRepository: AlterationRepository,
    tagRepository: TagRepository,
    dialogFactory: CharacterSheetAlterationDialogFactory,
    tagFactory: GMTagFactory,
) : ViewModel() {

    private val selectedCharacterSheetIdFlow = MutableStateFlow<String?>(null)
    private val selectedTagIdFlow = MutableStateFlow<String?>(null)

    private val _filter = createLwaTextFieldFlow(
        label = runBlocking { getString(Res.string.game_master__alteration__filter) },
    )
    private val filter = _filter.createLwaTextField()

    private val filteredAlterationsFlow: Flow<List<Alteration>> = combine(
        alterationRepository.alterationFlow.map { it.values },
        _filter.valueFlow.map { it.unAccent() },
        selectedTagIdFlow,
        dialogFactory::filterAlteration
    )

    private val tags: StateFlow<List<GMTagUio>> = combine(
        tagRepository.alterationsTagFlow(),
        selectedTagIdFlow,
    ) { tags, selectedTagId ->
        tagFactory.convertToGMTagItemUio(
            tags = tags.values,
            selectedTagId = selectedTagId,
        )
    }.stateIn(
        scope = viewModelScope,
        started = SharingStarted.Lazily,
        initialValue = emptyList(),
    )

    private val _errors = MutableSharedFlow<ErrorSnackUio>()
    val errors: SharedFlow<ErrorSnackUio> get() = _errors

    @OptIn(ExperimentalCoroutinesApi::class)
    val alterationDialog = selectedCharacterSheetIdFlow
        .flatMapLatest { characterSheetId ->
            combine(
                characterSheetRepository.characterDetailFlow(characterSheetId = characterSheetId),
                alterationRepository.activeAlterationsFlow(characterSheetId = characterSheetId),
                tagRepository.alterationsTagFlow(),
                filteredAlterationsFlow,
                selectedTagIdFlow,
            ) { characterSheet, activeAlterations, tagMap, alterations, selectedTagId ->
                dialogFactory.convertToDialogUio(
                    characterSheet = characterSheet,
                    tagMap = tagMap,
                    alterations = alterations,
                    activeAlterations = activeAlterations,
                    filter = filter,
                    tags = tags,
                    selectedTagId = selectedTagId,
                )
            }
        }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.Lazily,
            initialValue = null
        )

    fun show(characterSheetId: String?) {
        selectedCharacterSheetIdFlow.update { characterSheetId }
    }

    fun hide() {
        selectedCharacterSheetIdFlow.update { null }
    }

    fun selectedTag(id: String) {
        selectedTagIdFlow.update {
            when (it) {
                id -> null
                else -> id
            }
        }
    }

    suspend fun toggleAlteration(
        characterSheetId: String,
        alterationId: String,
        active: Boolean,
    ) {
        try {
            characterSheetRepository.updateAlteration(
                characterSheetId = characterSheetId,
                alterationId = alterationId,
                active = active.not(),
            )
        } catch (exception: Exception) {
            val message = ErrorSnackUio.from(exception = exception)
            _errors.emit(value = message)
        }
    }
}