package com.pixelized.desktop.lwa.ui.screen.admin.character.list

import com.pixelized.desktop.lwa.ui.screen.admin.character.list.GMCharacterItemUio.Action
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.campaign.Campaign
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetPreview
import java.awt.Desktop
import java.net.URI
import java.text.Collator

class GMCharacterFactory {

    private val isBrowserAvailable = Desktop.isDesktopSupported()
            && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)

    companion object {
        const val PLAYER_ID = "PLAYER"
        const val NPC_ID = "NPC"
    }

    fun filterCharacter(
        campaign: Campaign,
        characters: List<CharacterSheetPreview>,
        unAccentFilter: String,
        selectedTagId: String?,
    ): List<CharacterSheetPreview> {
        return characters.filter {
            val matchName = it.name.unAccent().contains(
                other = unAccentFilter,
                ignoreCase = true,
            )
            val matchJob = it.job.unAccent().contains(
                other = unAccentFilter,
                ignoreCase = true,
            )
            val matchTag = when (selectedTagId) {
                null -> true
                PLAYER_ID -> campaign.characters.contains(it.characterSheetId)
                NPC_ID -> campaign.npcs.contains(it.characterSheetId)
                else -> it.tags.contains(selectedTagId)
            }
            (matchName || matchJob) && matchTag
        }
    }

    suspend fun convertToGMCharacterPreviewUio(
        campaign: Campaign,
        characters: List<CharacterSheetPreview>,
        tagIdMap: Map<String, GMTagUio>,
    ): List<GMCharacterItemUio> {
        return characters.map {
            convertToGMCharacterPreviewUio(
                campaign = campaign,
                character = it,
                tagIdMap = tagIdMap,
            )
        }.sortedWith(compareBy(Collator.getInstance()) { it.name })
    }

    private suspend fun convertToGMCharacterPreviewUio(
        campaign: Campaign,
        character: CharacterSheetPreview,
        tagIdMap: Map<String, GMTagUio>,
    ): GMCharacterItemUio {
        val isPlayer = campaign.characters.contains(character.characterSheetId)
        val isNpc = campaign.npcs.contains(character.characterSheetId)

        val externalLink = if (isBrowserAvailable) {
            character.externalLink?.let { URI.create(it) }
        } else {
            null
        }

        // Build the call tag list.
        val tags = if (isPlayer) {
            listOfNotNull(tagIdMap[PLAYER_ID])
        } else {
            emptyList()
        } + if (isNpc) {
            listOfNotNull(tagIdMap[NPC_ID])
        } else {
            emptyList()
        } + character.tags.mapNotNull {
            tagIdMap[it]
        }

        // build the cell action list
        val actions = buildList {
            add(Action.DisplayPortrait)
            if (externalLink != null) {
                add(Action.ExternalLink(externalLink = externalLink))
            }
            when {
                isPlayer -> add(Action.RemoveFromGroup)
                isNpc -> add(Action.RemoveFromNpc)
                else -> {
                    add(Action.AddToGroup)
                    add(Action.AddToNpc)
                }
            }
            add(Action.DeleteCharacter(characterSheetId = character.characterSheetId))
        }
        // return the cell UIO.
        return GMCharacterItemUio(
            characterSheetId = character.characterSheetId,
            name = character.name,
            level = character.level,
            job = character.job.takeIf { it.isNotBlank() },
            tags = tags,
            actions = actions,
        )
    }
}