package com.pixelized.desktop.lwa.ui.screen.levelup

import androidx.compose.material.SnackbarDuration
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackUio
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.LevelUpDestination
import com.pixelized.desktop.lwa.ui.overlay.roll.RollResult
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId
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.stateIn
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.error__default__action
import lwacharactersheet.composeapp.generated.resources.error__missing_character_sheet__label
import org.jetbrains.compose.resources.getString

class LevelUpViewModel(
    private val characterSheetRepository: CharacterSheetRepository,
    private val levelUpFactory: LevelUpFactory,
    savedStateHandle: SavedStateHandle,
) : ViewModel() {
    private val argument = LevelUpDestination.Argument(savedStateHandle)

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

    private val results = MutableStateFlow<Map<String, RollResult.BoundedRollResult>>(emptyMap())
    private val selectedCharacteristicId = MutableStateFlow<String?>(null)

    val header = combine(
        characterSheetRepository.characterDetailFlow(characterSheetId = argument.characterSheetId),
        selectedCharacteristicId,
        levelUpFactory::convertToLevelUpHeaderUio
    ).stateIn(
        scope = viewModelScope,
        started = SharingStarted.Lazily,
        initialValue = null,
    )

    val characteristics = combine(
        characterSheetRepository.characterDetailFlow(characterSheetId = argument.characterSheetId),
        selectedCharacteristicId,
        levelUpFactory::convertToLevelUpCharacteristicUio,
    ).stateIn(
        scope = viewModelScope,
        started = SharingStarted.Lazily,
        initialValue = emptyList(),
    )

    val skills = combine(
        characterSheetRepository.characterDetailFlow(characterSheetId = argument.characterSheetId),
        results,
    ) { characterSheet, results ->
        levelUpFactory.convertToLevelUpSectionUio(
            characterSheetId = argument.characterSheetId,
            characterSheet = characterSheet,
            results = results,
        )
    }.stateIn(
        scope = viewModelScope,
        started = SharingStarted.Lazily,
        initialValue = null,
    )

    val displayCommitLevelUp: StateFlow<Boolean> = combine(
        skills,
        results,
        selectedCharacteristicId,
    ) { skills, results, selectedCharacteristicId ->
        val skillCount = skills?.let {
            val magicSkillsCount = it.magicSkills?.size ?: 0
            val commonSkillsCount = it.commonSkills?.size ?: 0
            val specialSkillsCount = it.specialSkills?.size ?: 0
            magicSkillsCount + commonSkillsCount + specialSkillsCount
        }
        skillCount == results.size && selectedCharacteristicId != null
    }.stateIn(
        scope = viewModelScope,
        started = SharingStarted.Lazily,
        initialValue = false,
    )

    fun applyRollResult(skillId: String, result: RollResult) {
        // Discard this case.
        if (result !is RollResult.BoundedRollResult) return
        // If not save the roll result to the map.
        results.value = results.value.toMutableMap().also {
            it[skillId] = result
        }
    }

    fun selectCharacteristic(characteristicId: String?) {
        if (selectedCharacteristicId.value == characteristicId) {
            selectedCharacteristicId.value = null
        } else {
            selectedCharacteristicId.value = characteristicId
        }
    }

    suspend fun commitLevelUp() {
        val characterSheet = characterSheetRepository.characterDetail(
            characterSheetId = argument.characterSheetId,
        )

        if (characterSheet == null) {
            _errors.emit(
                ErrorSnackUio(
                    message = getString(Res.string.error__missing_character_sheet__label),
                    action = getString(Res.string.error__default__action),
                    duration = SnackbarDuration.Long,
                )
            )
            return
        }

        val levelUpCharacter = characterSheet.copy(
            level = characterSheet.level + 1,
            shouldLevelUp = false,
            strength = characterSheet.strength + if (selectedCharacteristicId.value == CharacteristicId.STR) 1 else 0,
            dexterity = characterSheet.dexterity + if (selectedCharacteristicId.value == CharacteristicId.DEX) 1 else 0,
            constitution = characterSheet.constitution + if (selectedCharacteristicId.value == CharacteristicId.CON) 1 else 0,
            height = characterSheet.height + if (selectedCharacteristicId.value == CharacteristicId.HEI) 1 else 0,
            intelligence = characterSheet.intelligence + if (selectedCharacteristicId.value == CharacteristicId.INT) 1 else 0,
            power = characterSheet.power + if (selectedCharacteristicId.value == CharacteristicId.POW) 1 else 0,
            charisma = characterSheet.charisma + if (selectedCharacteristicId.value == CharacteristicId.CHA) 1 else 0,
            commonSkills = characterSheet.commonSkills.map { skill ->
                skill.copy(
                    level = skill.level + if (results.value.isSkillLeveledUp(skillId = skill.id)) 1 else 0,
                    used = false,
                )
            },
            specialSkills = characterSheet.specialSkills.map { skill ->
                skill.copy(
                    level = skill.level + if (results.value.isSkillLeveledUp(skillId = skill.id)) 1 else 0,
                    used = false,
                )
            },
            magicSkills = characterSheet.magicSkills.map { skill ->
                skill.copy(
                    level = skill.level + if (results.value.isSkillLeveledUp(skillId = skill.id)) 1 else 0,
                    used = false,
                )
            },
        )

        characterSheetRepository.updateCharacter(
            sheet = levelUpCharacter,
            create = false,
        )
    }
}