package com.pixelized.server.lwa.server

import com.pixelized.server.lwa.logics.ItemUsageLogic
import com.pixelized.server.lwa.services.alteration.AlterationService
import com.pixelized.server.lwa.services.alteration.AlterationStore
import com.pixelized.server.lwa.services.campaign.CampaignService
import com.pixelized.server.lwa.services.campaign.CampaignStore
import com.pixelized.server.lwa.services.character.CharacterSheetService
import com.pixelized.server.lwa.services.character.CharacterSheetStore
import com.pixelized.server.lwa.services.inventory.InventoryService
import com.pixelized.server.lwa.services.inventory.InventoryStore
import com.pixelized.server.lwa.services.item.ItemService
import com.pixelized.server.lwa.services.item.ItemStore
import com.pixelized.server.lwa.services.map.MapService
import com.pixelized.server.lwa.services.map.MapStore
import com.pixelized.server.lwa.services.tag.TagService
import com.pixelized.server.lwa.services.tag.TagStore
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
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 kotlinx.coroutines.flow.MutableSharedFlow

class Engine(
    val characterService: CharacterSheetService,
    val campaignService: CampaignService,
    val alterationService: AlterationService,
    val itemService: ItemService,
    val inventoryService: InventoryService,
    val tagService: TagService,
    val mapService: MapService,
    val campaignJsonFactory: CampaignJsonFactory,
    val itemUsageLogic: ItemUsageLogic,
    private val campaignStore: CampaignStore,
    private val characterStore: CharacterSheetStore,
    private val alterationStore: AlterationStore,
    private val itemStore: ItemStore,
    private val inventoryStore: InventoryStore,
    private val tagStore: TagStore,
    private val mapStore: MapStore,
) {
    val webSocket = MutableSharedFlow<SocketMessage>()

    suspend fun handle(message: SocketMessage) {
        try {
            unSafeHandle(message)
        } catch (exception: Exception) {
            println(exception.message) // TODO proper exception handling
        }
    }

    private suspend fun unSafeHandle(message: SocketMessage) {
        when (message) {

            is RollEvent -> Unit // Nothing to do here.

            is GameMasterEvent -> when (message) {
                is GameMasterEvent.TogglePlayer -> campaignService.updateToggleParty()
                is GameMasterEvent.ToggleNpc -> campaignService.updateToggleNpc()
                is GameMasterEvent.DisplayPortrait -> Unit // Nothing to do here.
            }

            is CharacterSheetEvent -> when (message) {
                is CharacterSheetEvent.UpdateAlteration -> characterService.updateAlteration(
                    characterSheetId = message.characterSheetId,
                    alterationId = message.alterationId,
                    active = message.active,
                )

                is CharacterSheetEvent.UpdateDamage -> characterService.updateDamage(
                    characterSheetId = message.characterSheetId,
                    damage = message.damage,
                )

                is CharacterSheetEvent.UpdateDiminished -> characterService.updateDiminished(
                    characterSheetId = message.characterSheetId,
                    diminished = message.diminished,
                )

                is CharacterSheetEvent.UpdateFatigue -> characterService.updateFatigue(
                    characterSheetId = message.characterSheetId,
                    fatigue = message.fatigue,
                )

                is CharacterSheetEvent.UpdateSkillUsageEvent -> characterService.updateCharacterSkillUsage(
                    characterSheetId = message.characterSheetId,
                    skillId = message.skillId,
                    used = message.used,
                )
            }

            is CampaignEvent -> when (message) {
                is CampaignEvent.CharacterAdded -> campaignService.addCharacter(
                    characterSheetId = message.characterSheetId,
                )

                is CampaignEvent.CharacterRemoved -> campaignService.removeCharacter(
                    characterSheetId = message.characterSheetId,
                )

                is CampaignEvent.NpcAdded -> campaignService.addNpc(
                    characterSheetId = message.characterSheetId,
                )

                is CampaignEvent.NpcRemoved -> campaignService.removeNpc(
                    characterSheetId = message.characterSheetId,
                )

                is CampaignEvent.MapUpdated -> {
                    // convert the map into the a usable data model.
                    val map = campaignJsonFactory.convertFromJson(json = message.map)
                    // update the map
                    campaignService.setMap(map = map)
                }

                is CampaignEvent.MapDeleted -> {
                    // update the map
                    campaignService.setMap(map = null)
                }

                is CampaignEvent.SceneUpdated -> Unit
            }

            is GameAdminEvent -> when (message) {
                is GameAdminEvent.ServerSynchronization -> {
                    characterStore.updateCharacterFlow()
                    campaignStore.updateCampaignFlow()
                    alterationStore.updateAlterationFlow()
                    itemStore.updateItemsFlow()
                    inventoryStore.updateInventoryFlow()
                    tagStore.updateTagFlow()
                    mapStore.updateMapFlow()

                    webSocket.emit(
                        value = GameAdminEvent.ServerSynchronization(
                            timestamp = System.currentTimeMillis(),
                        ),
                    )
                }
            }

            is ApiSynchronisation -> Unit // Nothing to do there.
        }
    }
}