package com.pixelized.shared.lwa.usecase


import com.pixelized.shared.lwa.model.AlteredCharacterSheet
import com.pixelized.shared.lwa.model.alteration.FieldAlteration
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
import com.pixelized.shared.lwa.parser.expression.Expression
import com.pixelized.shared.lwa.parser.expression.ExpressionParser
import com.pixelized.shared.lwa.parser.word.Word
import com.pixelized.shared.lwa.utils.floor5
import kotlin.math.max
import kotlin.math.min

class ExpressionUseCase(
    private val expressionParser: ExpressionParser,
    private val characterSheetUseCase: CharacterSheetUseCase,
    private val rollUseCase: RollUseCase,
) {
    fun computeSkillValue(
        sheet: AlteredCharacterSheet,
        skill: CharacterSheet.Skill,
        alterations: Map<String, List<FieldAlteration>>,
        diminished: Int,
    ): Int {
        val context = Context(
            sheet = sheet,
        )

        val base: Int = context.evaluate(
            expression = skill.base.let(expressionParser::parse),
        ).let {
            when (skill.occupation) {
                true -> max(MIN_OCCUPATION_VALUE, it)
                else -> it
            }
        }

        val bonus = context.evaluate(
            expression = skill.bonus?.let(expressionParser::parse),
        )

        val level = max((skill.level - 1) * 5, 0)

        val alteration = alterations[skill.id]?.sumOf {
            context.evaluate(it.expression)
        } ?: 0

        return max(base + bonus + level + alteration - diminished, 0)
    }

    fun computeRoll(
        sheet: AlteredCharacterSheet,
        expression: String,
    ): Int {
        print("Roll::$expression::")
        val roll = expressionParser.parse(input = expression)?.let {
            computeExpression(
                sheet = sheet,
                expression = it,
            )
        } ?: 0
        println("::$roll")
        return roll
    }

    fun computeExpression(
        sheet: AlteredCharacterSheet,
        expression: Expression,
    ): Int {
        val context = Context(
            sheet = sheet,
        )
        return context.evaluate(
            expression = expression,
        )
    }

    private fun Context.evaluate(expression: Expression?): Int {

        fun List<FieldAlteration>?.sum() = this?.sumOf { evaluate(it.expression) } ?: 0

        return when (expression) {
            is Expression.Add -> {
                evaluate(expression.first) + evaluate(expression.second)
            }

            is Expression.Minus -> {
                evaluate(expression.first) - evaluate(expression.second)
            }

            is Expression.Div -> {
                evaluate(expression.first) / evaluate(expression.second)
            }

            is Expression.Prod -> {
                evaluate(expression.first) * evaluate(expression.second)
            }

            is Expression.Inversion -> {
                -evaluate(expression.expression)
            }

            is Expression.Maximum -> {
                max(evaluate(expression.first), evaluate(expression.second))
            }

            is Expression.Minimum -> {
                min(evaluate(expression.first), evaluate(expression.second))
            }

            is Expression.Floor5 -> {
                evaluate(expression.expression).floor5()
            }

            is Expression.Flat -> {
                expression.value
            }

            is Expression.DiceExpression -> {
                rollUseCase.roll(expression.dice)
            }

            is Expression.UrlExpression -> {
                0 // Ignore this case.
            }

            is Expression.WordExpression -> when (expression.word.type) {
                Word.Type.BDC -> evaluate(
                    expression = expressionParser.parse(
                        characterSheetUseCase.meleeBonusDamage(
                            strength = sheet.strength,
                            height = sheet.height,
                        )
                    )
                )

                Word.Type.BDD -> evaluate(
                    expression = expressionParser.parse(
                        characterSheetUseCase.distanceBonusDamage(
                            strength = sheet.strength,
                            height = sheet.height,
                        )
                    )
                )

                Word.Type.STR -> sheet.strength
                Word.Type.DEX -> sheet.dexterity
                Word.Type.CON -> sheet.constitution
                Word.Type.HEI -> sheet.height
                Word.Type.INT -> sheet.intelligence
                Word.Type.POW -> sheet.power
                Word.Type.CHA -> sheet.charisma
            }

            null -> 0
        }
    }

    data class Context(
        val sheet: AlteredCharacterSheet,
    )

    companion object {
        private const val MIN_OCCUPATION_VALUE = 40
    }
}