package com.pixelized.desktop.lwa.ui.screen.campaign.chatbox

import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.settings.model.Settings
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.CharacteristicTextMessageUio
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.DiminishedTextMessageUio
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.PurseTextMessageUio
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.RollTextMessageUio
import com.pixelized.desktop.lwa.ui.screen.campaign.chatbox.messages.TextMessage
import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.protocol.websocket.ApiSynchronisation
import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent
import com.pixelized.shared.lwa.protocol.websocket.CharacterSheetEvent
import com.pixelized.shared.lwa.protocol.websocket.GameAdminEvent
import com.pixelized.shared.lwa.protocol.websocket.GameMasterEvent
import com.pixelized.shared.lwa.protocol.websocket.RollEvent
import com.pixelized.shared.lwa.protocol.websocket.SocketMessage
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__hp_down
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__hp_down_armor
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__hp_up
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__pp_down
import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change__pp_up
import lwacharactersheet.composeapp.generated.resources.chat__purse_change__earn
import lwacharactersheet.composeapp.generated.resources.chat__purse_change__spend
import java.text.SimpleDateFormat
import kotlin.math.abs

class TextMessageFactory(
    private val characterSheetRepository: CharacterSheetRepository,
) {
    private val formatTime = SimpleDateFormat("HH:mm:ss")

    suspend fun convertToTextMessage(
        settings: Settings,
        campaign: Campaign,
        message: SocketMessage,
    ): TextMessage? {
        return when (message) {
            is RollEvent -> {
                val sheetPreview = characterSheetRepository
                    .characterPreview(characterSheetId = message.characterSheetId)
                    ?: return null

                val isGm = settings.isGameMaster ?: false

                RollTextMessageUio(
                    id = "${message.timestamp}-${message.characterSheetId}-${message.uuid}",
                    timestamp = formatTime.format(message.timestamp),
                    character = sheetPreview.name,
                    skillLabel = message.skillLabel,
                    rollDifficulty = message.rollDifficulty,
                    rollValue = message.rollValue,
                    rollSuccessLimit = takeIf { isGm }?.let { message.rollSuccessLimit },
                    resultLabel = message.resultLabel,
                    resultType = when (message.critical) {
                        RollEvent.Critical.CRITICAL_SUCCESS -> RollTextMessageUio.Critical.CRITICAL_SUCCESS
                        RollEvent.Critical.SPECIAL_SUCCESS -> RollTextMessageUio.Critical.SPECIAL_SUCCESS
                        RollEvent.Critical.SUCCESS -> RollTextMessageUio.Critical.SUCCESS
                        RollEvent.Critical.FAILURE -> RollTextMessageUio.Critical.FAILURE
                        RollEvent.Critical.CRITICAL_FAILURE -> RollTextMessageUio.Critical.CRITICAL_FAILURE
                        null -> null
                    }
                )
            }

            is CharacterSheetEvent -> when (message) {

                is CharacterSheetEvent.UpdateDiminished -> {
                    // only display the message if the character is in the party or npcs.
                    val isInParty = campaign.characters.contains(message.characterSheetId)
                    val isInNpcs = campaign.npcs.contains(message.characterSheetId)
                    if ((isInParty || isInNpcs).not()) return null
                    // get the character sheet
                    val sheetPreview = characterSheetRepository
                        .characterPreview(characterSheetId = message.characterSheetId)
                        ?: return null

                    DiminishedTextMessageUio(
                        id = "${message.timestamp}-${message.characterSheetId}-Diminished",
                        timestamp = formatTime.format(message.timestamp),
                        character = sheetPreview.name,
                        diminished = message.diminished,
                    )
                }

                is CharacterSheetEvent.UpdateDamage -> {
                    // only display the message if the character is in the party or npcs.
                    val isInParty = campaign.characters.contains(message.characterSheetId)
                    val isInNpcs = campaign.npcs.contains(message.characterSheetId)
                    if ((isInParty || isInNpcs).not()) return null
                    // get the character sheet
                    val sheet = characterSheetRepository
                        .characterDetail(characterSheetId = message.characterSheetId)
                        ?: return null
                    // We are talking about damage / consumption there so old value have to be put first.
                    val value = message.oldValue - message.damage
                    val armor = message.armor

                    CharacteristicTextMessageUio(
                        id = "${message.timestamp}-${message.characterSheetId}-Damage",
                        timestamp = formatTime.format(message.timestamp),
                        label = when {
                            value >= 0 -> Res.string.chat__characteristic_change__hp_up
                            armor != null -> Res.string.chat__characteristic_change__hp_down_armor
                            else -> Res.string.chat__characteristic_change__hp_down
                        },
                        character = sheet.name,
                        value = abs(value),
                        armor = message.armor,
                    )
                }

                is CharacterSheetEvent.UpdateFatigue -> {
                    // only display the message if the character is in the party.
                    val isInParty = campaign.characters.contains(message.characterSheetId)
                    if (isInParty.not()) return null
                    // get the character sheet
                    val sheet = characterSheetRepository
                        .characterDetail(characterSheetId = message.characterSheetId)
                        ?: return null
                    // We are talking about damage / consumption there so old value have to be put first.
                    val value = message.oldValue - message.fatigue

                    CharacteristicTextMessageUio(
                        id = "${message.timestamp}-${message.characterSheetId}-Fatigue",
                        timestamp = formatTime.format(message.timestamp),
                        label = when {
                            value < 0 -> Res.string.chat__characteristic_change__pp_down
                            else -> Res.string.chat__characteristic_change__pp_up
                        },
                        character = sheet.name,
                        value = abs(value),
                        armor = 0,
                    )
                }

                is CharacterSheetEvent.UpdateSkillUsageEvent -> null
                is CharacterSheetEvent.UpdateAlteration -> null
            }

            is CampaignEvent -> when (message) {
                is CampaignEvent.CharacterAdded -> null
                is CampaignEvent.CharacterRemoved -> null
                is CampaignEvent.NpcAdded -> null
                is CampaignEvent.NpcRemoved -> null
                is CampaignEvent.SceneUpdated -> null
                is CampaignEvent.MapUpdated -> null
                is CampaignEvent.MapDeleted -> null
            }

            is GameMasterEvent -> null

            is GameAdminEvent -> null

            is ApiSynchronisation -> when (message) {

                is ApiSynchronisation.PurseUpdate -> {
                    // only display the message if the character is in the party.
                    val isInParty = campaign.characters.contains(message.characterSheetId)
                    if (isInParty.not()) return null
                    // get the character sheet
                    val sheet = characterSheetRepository
                        .characterDetail(characterSheetId = message.characterSheetId)
                        ?: return null

                    PurseTextMessageUio(
                        id = "${message.timestamp}-${message.characterSheetId}-Purse",
                        timestamp = formatTime.format(message.timestamp),
                        character = sheet.name,
                        action = when (message.add) {
                            true -> Res.string.chat__purse_change__earn
                            else -> Res.string.chat__purse_change__spend
                        },
                        gold = message.gold.takeIf { it != 0 }?.let { abs(it) },
                        silver = message.silver.takeIf { it != 0 }?.let { abs(it) },
                        copper = message.copper.takeIf { it != 0 }?.let { abs(it) },
                    )
                }

                else -> null
            }
        }
    }
}