package com.pixelized.server.lwa.services.character

import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJson
import com.pixelized.shared.lwa.model.characterSheet.factory.CharacterSheetJsonFactory
import com.pixelized.shared.lwa.protocol.rest.CharacterPreviewJson
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

class CharacterSheetService(
    private val characterStore: CharacterSheetStore,
    private val factory: CharacterSheetJsonFactory,
) {
    private val scope = CoroutineScope(Dispatchers.IO + Job())

    private val sheets get() = sheetsFlow.value

    private val sheetsFlow = characterStore.characterSheetsFlow()
        .map { entry -> entry.associateBy { character -> character.id } }
        .stateIn(
            scope = scope,
            started = SharingStarted.Eagerly,
            initialValue = emptyMap()
        )

    fun character(characterSheetId: String): CharacterSheet? {
        return sheets[characterSheetId]
    }

    fun charactersJson(characterSheetId: String): CharacterPreviewJson? {
        return sheets[characterSheetId]?.let { factory.convertToPreviewJson(sheet = it) }
    }

    fun charactersJson(): List<CharacterPreviewJson> {
        return sheets.map { factory.convertToPreviewJson(sheet = it.value) }
    }

    fun characterSheetJson(characterSheetId: String): CharacterSheetJson? {
        return sheets[characterSheetId]?.let(factory::convertToJson)
    }

    @Throws
    suspend fun save(
        character: CharacterSheetJson,
        create: Boolean,
    ) {
        return characterStore.save(
            sheet = factory.convertFromJson(character),
            create = create,
        )
    }

    @Throws
    suspend fun deleteCharacterSheet(
        characterSheetId: String,
    ) {
        characterStore.delete(
            characterSheetId = characterSheetId
        )
    }

    // Data manipulation through WebSocket.

    suspend fun updateAlteration(
        characterSheetId: String,
        alterationId: String,
        active: Boolean,
    ) {
        sheets[characterSheetId]?.let { character ->
            val contain = character.alterations.contains(alterationId)
            if (active && contain.not()) {
                val alterations = character.alterations.toMutableList().also {
                    it.add(alterationId)
                }
                characterStore.save(
                    sheet = character.copy(
                        alterations = alterations,
                    ),
                    create = false,
                )
            }
            if (active.not() && contain) {
                val alterations = character.alterations.toMutableList().also {
                    it.remove(alterationId)
                }
                characterStore.save(
                    sheet = character.copy(
                        alterations = alterations,
                    ),
                    create = false,
                )
            }
        }
    }

    suspend fun updateDamage(
        characterSheetId: String,
        damage: Int,
    ) {
        sheets[characterSheetId]?.let { character ->
            val update = character.copy(damage = damage)
            characterStore.save(
                sheet = update,
                create = false,
            )
        }
    }

    suspend fun updateDiminished(
        characterSheetId: String,
        diminished: Int,
    ) {
        sheets[characterSheetId]?.let { character ->
            val update = character.copy(diminished = diminished)
            characterStore.save(
                sheet = update,
                create = false,
            )
        }
    }

    suspend fun updateFatigue(
        characterSheetId: String,
        fatigue: Int,
    ) {
        sheets[characterSheetId]?.let { character ->
            val update = character.copy(fatigue = fatigue)
            characterStore.save(
                sheet = update,
                create = false,
            )
        }
    }

    suspend fun updateCharacterSkillUsage(
        characterSheetId: String,
        skillId: String,
        used: Boolean,
    ) {
        sheets[characterSheetId]?.let { character ->
            val update = character.copy(
                commonSkills = character.commonSkills.map { skill ->
                    skill.takeIf { skill.id == skillId }?.copy(used = used) ?: skill
                },
                specialSkills = character.specialSkills.map { skill ->
                    skill.takeIf { skill.id == skillId }?.copy(used = used) ?: skill
                },
                magicSkills = character.magicSkills.map { skill ->
                    skill.takeIf { skill.id == skillId }?.copy(used = used) ?: skill
                },
            )
            characterStore.save(
                sheet = update,
                create = false,
            )
        }
    }
}