/**
 * Determines the value to be displayed in the "Effect" column for boosters.
 */

package theorycrafter.ui.fiteditor.effectcolumn

import androidx.compose.runtime.Composable
import eve.data.millisAsTime
import eve.data.typeid.*
import theorycrafter.TheorycrafterContext
import theorycrafter.fitting.*
import theorycrafter.ui.fiteditor.effectcolumn.CommonEffectSources.anyAffectedModuleProperty
import theorycrafter.ui.widgets.TextAndTooltip
import theorycrafter.utils.with


/**
 * All the information we need to display a [Booster] effect.
 */
private class BoosterInfo(
    override val fit: Fit,
    override val appliedEffects: Map<AttributeProperty<*>, Collection<AppliedEffect>>,
    val booster: Booster,
): AffectingItemInfo


/**
 * Returns the displayed effect for the given booster.
 */
@Composable
fun displayedBoosterEffect(fit: Fit, booster: Booster): TextAndTooltip? {
    val appliedEffects = booster.appliedEffectsByTargetProperty()
    val boosterType = booster.type
    val boosterInfo = BoosterInfo(fit, appliedEffects, booster)

    return with(TheorycrafterContext.eveData, BoosterEffectSources, CommonEffectSources, boosterInfo) {
        when {
            boosterType.isBluePill() ->
                withMergedMainSource(
                    mainSource = SHIELD_BOOST_AMOUNT,
                    DURATION,
                    SIDE_EFFECT_CHANCE,
                    SHIELD_HP,
                    TURRET_OPTIMAL_RANGE,
                    CAPACITOR_CAPACITY,
                    MISSILE_EXPLOSION_VELOCITY,
                )
            boosterType.isExile() ->
                withMergedMainSource(
                    mainSource = ARMOR_REPAIR_AMOUNT,
                    DURATION,
                    SIDE_EFFECT_CHANCE,
                    ARMOR_HP,
                    CAPACITOR_CAPACITY,
                    TURRET_TRACKING_SPEED,
                    MISSILE_EXPLOSION_RADIUS
                )
            boosterType.isMindflood() ->
                withMergedMainSource(
                    mainSource = CAPACITOR_CAPACITY,
                    DURATION,
                    SIDE_EFFECT_CHANCE,
                    ARMOR_REPAIR_AMOUNT,
                    TURRET_OPTIMAL_RANGE,
                    MISSILE_EXPLOSION_RADIUS,
                    SHIELD_BOOST_AMOUNT
                )
            boosterType.isXInstinct() ->
                withMergedMainSource(
                    mainSource = SIGNATURE_RADIUS,
                    DURATION,
                    SIDE_EFFECT_CHANCE,
                    ARMOR_HP,
                    SHIELD_HP,
                    TURRET_FALLOFF,
                    MISSILE_VELOCITY
                )
            boosterType.isDrop() ->
                withMergedMainSource(
                    mainSource = TURRET_TRACKING_SPEED,
                    DURATION,
                    SIDE_EFFECT_CHANCE,
                    ARMOR_REPAIR_AMOUNT,
                    SHIELD_HP,
                    TURRET_FALLOFF,
                    MAX_VELOCITY
                )
            boosterType.isFrentix() ->
                withMergedMainSource(
                    mainSource = TURRET_OPTIMAL_RANGE,
                    DURATION,
                    SIDE_EFFECT_CHANCE,
                    ARMOR_HP,
                    MAX_VELOCITY,
                    TURRET_TRACKING_SPEED,
                    SHIELD_BOOST_AMOUNT
                )
            boosterType.isSoothSayer() ->
                withMergedMainSource(
                    mainSource = TURRET_FALLOFF,
                    DURATION,
                    SIDE_EFFECT_CHANCE,
                    ARMOR_REPAIR_AMOUNT,
                    SHIELD_HP,
                    TURRET_OPTIMAL_RANGE,
                    MAX_VELOCITY
                )
            boosterType.isCrash() ->
                withMergedMainSource(
                    mainSource = MISSILE_EXPLOSION_RADIUS,
                    DURATION,
                    SIDE_EFFECT_CHANCE,
                    ARMOR_HP,
                    MAX_VELOCITY,
                    MISSILE_VELOCITY,
                    SHIELD_BOOST_AMOUNT
                )
            boosterType.isAgencyHardshell() ->
                withoutMainSource(
                    allSourcesMissingText = "No armor repairers or shield boosters fitted",
                    ARMOR_REPAIR_AMOUNT,
                    SHIELD_BOOST_AMOUNT,
                    DURATION,
                )
            boosterType.isAgencyOverclocker() ->
                withMergedMainSource(MAX_VELOCITY, DURATION)
            boosterType.isAgencyPyrolancea() ->
                withoutMainSource(
                    allSourcesMissingText = "No turrets or missile launchers fitted",
                    TURRET_DAMAGE,
                    MISSILE_DAMAGE,
                    DURATION,
                )
            boosterType.isQuafeZeroClassic() ->
                withoutMainSource(
                    MAX_VELOCITY,
                    SCAN_RESOLUTION,
                    DURATION,
                )
            boosterType.isQuafeZeroGreenApple() ->
                withoutMainSource(
                    INERTIA_MODIFIER,
                    CAPACITOR_RECHARGE_TIME,
                    DURATION,
                )
            boosterType.isAntipharmakonToxot() ->
                withMergedMainSource(MISSILE_FLIGHT_TIME, DURATION)
            boosterType.isVeilguard() ->
                withMergedMainSource(STABILIZED_CLOAK_DURATION, DURATION)

            // AIR Boosters are poorly structured.
            // - Some only have "II" versions.
            // - Some have "II" and "III" versions, but they do boost completely different attributes.
            // So instead of making sense of it, we just dump all applicable sources.
            boosterType.isAIR() ->
                withoutMainSource(
                    INERTIA_MODIFIER, // AIR Agility Booster II and AIR Overclocker Booster III
                    SCAN_TIME_EFFECT,  // AIR Astro-Acquisition II Booster
                    SCAN_DEVIATION_EFFECT,  // AIR Astro-Pinpointing II Booster
                    SCAN_SENSOR_STRENGTH_EFFECT,  // AIR Astro-Rangefinding II Booster
                    ARMOR_REPAIR_AMOUNT,  // AIR Hardshell Booster II
                    SHIELD_BOOST_AMOUNT,  // AIR Hardshell Booster II
                    MINING_LASER_OPTIMAL_RANGE,  // AIR Mining Range Booster (I and II)
                    MINING_AMOUNT,  // AIR Mining Yield Booster (I, II and III)
                    SHIELD_HP,  // AIR Nirvana Booster II
                    MAX_VELOCITY,  // AIR Overclocker Booster II
                    CAPACITOR_RECHARGE_TIME,  // AIR Overclocker Booster III, AIR Rapture Booster II
                    TURRET_DAMAGE,  // AIR Pyrolancea Booster II
                    MISSILE_DAMAGE,  // AIR Pyrolancea Booster II
                    SHIELD_BOOSTER_DURATION,  // AIR Repairer Booster III
                    ARMOR_REPAIRER_DURATION,  // AIR Repairer Booster III
                    DURATION
                )

            boosterType.isWightstorm() -> {
                when {
                    boosterType.isWightstormCetana() ->
                        withMergedMainSource(AFTERBURNER_SPEED_FACTOR_BONUS)
                    boosterType.isWightstormManasikara() ->
                        withMergedMainSource(ENERGY_WEAPONS_TRACKING_SPEED)
                    boosterType.isWightstormNirvana() ->
                        withMergedMainSource(SHIELD_HP)
                    boosterType.isWightstormRapture() ->
                        withMergedMainSource(CAPACITOR_RECHARGE_TIME)
                    boosterType.isWightstormSunyata() ->
                        withMergedMainSource(HEAT_DAMAGE)
                    boosterType.isWightstormVitarka() ->
                        withMergedMainSource(ENERGY_WEAPONS_DAMAGE)
                    else -> null
                }
            }

            else -> null
        }
    }
}


/**
 * Specifies the property and how to display it.
 */
private class BoosterPropertyEffectSource<T: Any>(


    /**
     * Returns the property.
     */
    val property: (Booster) -> AttributeProperty<T>?,


    /**
     * A very short description of the property.
     */
    description: String,


    /**
     * Formats the property's value.
     */
    private val formatValue: (T, isCellDisplay: Boolean) -> String?


): DisplayedEffectSource<BoosterInfo>(description = description) {


    /**
     * A constructor that takes a [formatValue] function which doesn't care about whether the value will be displayed
     * in the cell or the tooltip.
     */
    constructor(
        property: (Booster) -> AttributeProperty<T>?,
        description: String,
        formatValue: (T) -> String?
    ): this(
        property = property,
        description = description,
        formatValue = { value, _ -> formatValue(value) }
    )


    @Composable
    override fun valueOrMissingText(
        fit: Fit,
        affectingItemInfo: BoosterInfo,
        isCellDisplay: Boolean
    ): ValueOrMissing?{
        val property = property(affectingItemInfo.booster) ?: return null
        val valueText = formatValue(property.value, isCellDisplay)
        return valueText.valueOr(null)
    }


}



/**
 * The sources of booster effects.
 */
private object BoosterEffectSources {


    /**
     * The effect on stabilized cloak duration.
     */
    val STABILIZED_CLOAK_DURATION = AppliedEffectSource.fromOptionalProperty(
        property = { fit, affectedProperties ->
            fit.anyAffectedModuleProperty(affectedProperties, Module::stabilizeCloakDuration)
        },
        description = "stable cloak duration",
        absoluteValue = { value, _ -> (1000*value).millisAsTime() },
        missingText = "No cloaking device fitted"
    )


    /**
     * The effect on any missile damage. Note that this is the effect on the charges rather than on the missile damage
     * multiplier in the character.
     */
    val MISSILE_DAMAGE = CommonEffectSources.missileDamage(
        description = "missile damage",
        missingText = "No missiles fitted"
    )


    /**
     * The duration of the booster.
     */
    val DURATION = BoosterPropertyEffectSource(
        property = Booster::duration,
        description = "duration",
        formatValue = { value -> value.millisAsTime() }
    )


    /**
     * The chance of side effects.
     */
    val SIDE_EFFECT_CHANCE = BoosterPropertyEffectSource(
        property = Booster::sideEffectChance,
        description = "chance of side effect",
        formatValue = { value, isCellDisplay -> value.fractionText(isCellDisplay, withSign = false) }
    )


}
