package com.pixelized.shared.lwa.model

import com.pixelized.shared.lwa.model.alteration.FieldAlteration
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.ARMOR
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.CHA
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.CON
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.DEX
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.DMG
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.GHP
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.HEI
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.HP
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.INITIATIVE
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.INT
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.LB
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.LVL
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.MOV
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.PORTRAIT
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.POW
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.PP
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.REFLEX
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.STR
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.CharacteristicId.THUMBNAIL
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet.StatusId.DIMINISHED
import com.pixelized.shared.lwa.parser.expression.Expression
import com.pixelized.shared.lwa.usecase.CharacterSheetUseCase
import com.pixelized.shared.lwa.usecase.ExpressionUseCase
import kotlin.math.max

class AlteredCharacterSheetFactory(
    private val sheetUseCase: CharacterSheetUseCase,
    private val expressionUseCase: ExpressionUseCase,
) {
    fun sheet(
        characterSheet: CharacterSheet,
        alterations: Map<String, List<FieldAlteration>>,
    ): AlteredCharacterSheet {
        return AlteredCharacterSheet(
            sheetUseCase = sheetUseCase,
            expressionUseCase = expressionUseCase,
            sheet = characterSheet,
            fieldAlterations = alterations,
        )
    }
}

class AlteredCharacterSheet(
    private val sheetUseCase: CharacterSheetUseCase,
    private val expressionUseCase: ExpressionUseCase,
    private val sheet: CharacterSheet,
    private val fieldAlterations: Map<String, List<FieldAlteration>>,
) {
    val id: String = sheet.id

    val name: String = sheet.name

    val shouldLevelUp: Boolean = sheet.shouldLevelUp

    val portrait: String?
        get() = fieldAlterations[PORTRAIT]
            ?.firstNotNullOfOrNull { (it.expression as? Expression.UrlExpression)?.url }
            ?: sheet.portrait

    val thumbnail: String?
        get() = fieldAlterations[THUMBNAIL]
            ?.firstNotNullOfOrNull { (it.expression as? Expression.UrlExpression)?.url }
            ?: sheet.thumbnail

    val level: Int
        get() = sheet.level + fieldAlterations[LVL].sum()

    val strength: Int
        get() = sheet.strength + fieldAlterations[STR].sum()

    val dexterity: Int
        get() = sheet.dexterity + fieldAlterations[DEX].sum()

    val constitution: Int
        get() = sheet.constitution + fieldAlterations[CON].sum()

    val height: Int
        get() = sheet.height + fieldAlterations[HEI].sum()

    val intelligence: Int
        get() = sheet.intelligence + fieldAlterations[INT].sum()

    val power: Int
        get() = sheet.power + fieldAlterations[POW].sum()

    val charisma: Int
        get() = sheet.charisma + fieldAlterations[CHA].sum()

    val maxHp: Int
        get() = sheetUseCase.maxHp(
            constitution = constitution,
            height = height,
            level = level,
        ) + fieldAlterations[HP].sum()

    val maxPp: Int
        get() = sheetUseCase.maxPp(
            power = power,
        ) + fieldAlterations[PP].sum()

    val damage: Int
        get() = sheet.damage

    val fatigue: Int
        get() = sheet.fatigue

    val diminished: Int
        get() = max(0, sheet.diminished + diminishedAlterations)

    val diminishedAlterations: Int
        get() = fieldAlterations[DIMINISHED].sum()

    val alterations: List<String>
        get() = sheet.alterations

    val movement: Int
        get() = sheetUseCase.movement() + fieldAlterations[MOV].sum()

    val armor: Int
        get() = sheetUseCase.armor() + fieldAlterations[ARMOR].sum()

    val reflex: Int
        get() = sheetUseCase.reflex(
            strength = strength,
            dexterity = dexterity,
            intelligence = intelligence,
        ) + fieldAlterations[REFLEX].sum()

    val initiative: Int
        get() = sheetUseCase.initiative(dexterity = dexterity) + fieldAlterations[INITIATIVE].sum()

    val damageBonus: String
        get() {
            val initial = sheetUseCase.meleeBonusDamage(
                strength = strength,
                height = height,
            )
            return fieldAlterations[DMG]
                ?.joinToString(separator = "+") { it.expression.toString() }
                ?.let { "$initial+$it" }
                ?: initial
        }

    val learning: Int
        get() = sheetUseCase.learning(
            intelligence = intelligence
        ) + fieldAlterations[LB].sum()

    val hpGrow: Int
        get() = sheetUseCase.hpGrow(
            constitution = constitution,
        ) + fieldAlterations[GHP].sum()

    // Helper method

    private fun List<FieldAlteration>?.sum() = this?.sumOf {
        expressionUseCase.computeExpression(
            sheet = this@AlteredCharacterSheet,
            expression = it.expression
        )
    } ?: 0
}