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

package theorycrafter.ui.fiteditor.effectcolumn

import androidx.compose.runtime.Composable
import eve.data.AttributeModifier.Operation
import eve.data.DamageType
import eve.data.TacticalModeType
import eve.data.typeid.*
import theorycrafter.fitting.AppliedEffect
import theorycrafter.fitting.AttributeProperty
import theorycrafter.fitting.Fit
import theorycrafter.fitting.TacticalMode
import theorycrafter.ui.widgets.TextAndTooltip
import theorycrafter.utils.with


/**
 * All the information we need to display a [TacticalMode]'s effect.
 */
private class TacticalModeInfo(
    override val fit: Fit,
    val tacticalMode: TacticalMode
): AffectingItemInfo{

    // We're only displaying tactical mode properties, not applied effects
    override val appliedEffects: Map<AttributeProperty<*>, Collection<AppliedEffect>>
        get() = error("Tactical modes don't display their applied effects")

}


/**
 * Returns the displayed effect for the given tactical mode.
 */
@Composable
fun displayedTacticalModeEffect(fit: Fit, tacticalMode: TacticalMode): TextAndTooltip? {
    val shipType = fit.ship.type
    val tacticalModeKind = tacticalMode.type.kind
    val tacticalModeInfo = TacticalModeInfo(fit, tacticalMode)
    return with(TacticalModeEffectSources, tacticalModeInfo) {
        when {
            shipType.isJackdaw() -> when (tacticalModeKind) {
                TacticalModeType.Kind.DEFENSE -> withMergedMainSource(
                    mainSource = SHIELD_RESISTANCE_BONUS_PROPERTY,
                    SIGNATURE_RADIUS_BONUS_PROPERTY
                )
                TacticalModeType.Kind.SHARPSHOOTER -> withMergedMainSource(
                    mainSource = ROCKET_AND_LIGHT_MISSILE_DAMAGE_BONUS_PROPERTY,
                    ROCKET_AND_LIGHT_MISSILE_VELOCITY_BONUS_PROPERTY,
                    SENSOR_STRENGTH_BONUS_PROPERTY,
                    TARGETING_RANGE_BONUS_PROPERTY,
                    EWAR_RESISTANCE_BONUS_PROPERTY,
                )
                TacticalModeType.Kind.PROPULSION -> withMergedMainSource(
                    mainSource = MAX_VELOCITY_BONUS_PROPERTY,
                    INERTIA_MODIFIER_BONUS_PROPERTY
                )
            }
            shipType.isSvipul() -> when (tacticalModeKind) {
                TacticalModeType.Kind.DEFENSE -> withMergedMainSource(
                    mainSource = SHIELD_AND_ARMOR_RESISTANCE_BONUS_PROPERTY,
                    MICROWARPDRIVE_SIGNATURE_RADIUS_BONUS_PROPERTY,
                    REMOTE_SHIELD_BOOSTER_AMOUNT_BONUS_PROPERTY,
                    REMOTE_SHIELD_BOOSTER_CAPACITOR_NEED_BONUS_PROPERTY,
                )
                TacticalModeType.Kind.SHARPSHOOTER -> withMergedMainSource(
                    mainSource = SMALL_PROJECTILE_TURRET_DAMAGE_BONUS_PROPERTY,
                    SMALL_PROJECTILE_TURRET_TRACKING_BONUS_PROPERTY,
                    SENSOR_STRENGTH_BONUS_PROPERTY,
                    TARGETING_RANGE_BONUS_PROPERTY,
                    EWAR_RESISTANCE_BONUS_PROPERTY,
                )
                TacticalModeType.Kind.PROPULSION -> withMergedMainSource(
                    mainSource = AFTERBURNER_AND_MICROWARPDRIVE_SPEED_BOOST_BONUS_PROPERTY,
                    INERTIA_MODIFIER_BONUS_PROPERTY
                )
            }
            shipType.isConfessor() -> when (tacticalModeKind) {
                TacticalModeType.Kind.DEFENSE -> withMergedMainSource(
                    mainSource = ARMOR_RESISTANCE_BONUS_PROPERTY,
                    SIGNATURE_RADIUS_BONUS_PROPERTY,
                    REMOTE_ARMOR_REPAIRER_AMOUNT_BONUS_PROPERTY,
                    REMOTE_ARMOR_REPAIRER_CAPACITOR_NEED_BONUS_PROPERTY,
                )
                TacticalModeType.Kind.SHARPSHOOTER -> withMergedMainSource(
                    mainSource = SMALL_ENERGY_TURRET_DAMAGE_BONUS_PROPERTY,
                    SMALL_ENERGY_TURRET_OPTIMAL_RANGE_BONUS_PROPERTY,
                    SENSOR_STRENGTH_BONUS_PROPERTY,
                    TARGETING_RANGE_BONUS_PROPERTY,
                    EWAR_RESISTANCE_BONUS_PROPERTY,
                )
                TacticalModeType.Kind.PROPULSION -> withMergedMainSource(
                    mainSource = AFTERBURNER_AND_MICROWARPDRIVE_SPEED_BOOST_BONUS_PROPERTY,
                    INERTIA_MODIFIER_BONUS_PROPERTY
                )
            }
            shipType.isHecate() -> when (tacticalModeKind) {
                TacticalModeType.Kind.DEFENSE -> withMergedMainSource(
                    mainSource = ARMOR_AND_HULL_RESISTANCE_BONUS_PROPERTY,
                    ARMOR_REPAIRER_DURATION_BONUS_PROPERTY
                )
                TacticalModeType.Kind.SHARPSHOOTER -> withMergedMainSource(
                    mainSource = SMALL_HYBRID_TURRET_DAMAGE_BONUS_PROPERTY,
                    SMALL_HYBRID_TURRET_OPTIMAL_RANGE_BONUS_PROPERTY,
                    SENSOR_STRENGTH_BONUS_PROPERTY,
                    TARGETING_RANGE_BONUS_PROPERTY,
                    EWAR_RESISTANCE_BONUS_PROPERTY,
                )
                TacticalModeType.Kind.PROPULSION -> withMergedMainSource(
                    mainSource = MICROWARPDRIVE_SPEED_BOOST_BONUS_PROPERTY,
                    MICROWARPDRIVE_CAPACITOR_NEED_BONUS_PROPERTY,
                    INERTIA_MODIFIER_BONUS_PROPERTY
                )
            }
            shipType.isSkua() -> when (tacticalModeKind) {
                TacticalModeType.Kind.DEFENSE -> withMergedMainSource(
                    mainSource = SHIELD_RESISTANCE_BONUS_PROPERTY,
                    SIGNATURE_RADIUS_BONUS_PROPERTY,
                    SHIELD_RECHARGE_TIME_BONUS_PROPERTY
                )
                TacticalModeType.Kind.SHARPSHOOTER -> withMergedMainSource(
                    mainSource = ROCKET_AND_LIGHT_MISSILE_DAMAGE_BONUS_PROPERTY,
                    SKUA_LIGHT_MISSILE_VELOCITY_BONUS_PROPERTY,
                    SKUA_ROCKET_VELOCITY_BONUS_PROPERTY,
                    SENSOR_STRENGTH_BONUS_PROPERTY,
                    TARGETING_RANGE_BONUS_PROPERTY,
                    EWAR_RESISTANCE_BONUS_PROPERTY,
                )
                TacticalModeType.Kind.PROPULSION -> withMergedMainSource(
                    mainSource = AFTERBURNER_AND_MICROWARPDRIVE_SPEED_BOOST_BONUS_PROPERTY,
                    INERTIA_MODIFIER_BONUS_PROPERTY
                )
            }
            shipType.isAnhinga() -> when (tacticalModeKind) {
                TacticalModeType.Kind.DEFENSE -> withMergedMainSource(  // Primary mode
                    mainSource = ANHINGA_MISSILE_LAUNCHER_ROF_BONUS_PROPERTY,
                    ANHINGA_MISSILE_VELOCITY_PENALTY_PROPERTY,
                    ANHINGA_MISSILE_FLIGHT_TIME_BONUS_PROPERTY,
                    TARGETING_RANGE_BONUS_PROPERTY,
                )
                TacticalModeType.Kind.SHARPSHOOTER -> withMergedMainSource(
                    mainSource = ANHINGA_MISSILE_LAUNCHER_ROF_BONUS_PROPERTY,
                    ANHINGA_MISSILE_FLIGHT_TIME_PENALTY_PROPERTY
                )
                TacticalModeType.Kind.PROPULSION -> withMergedMainSource(
                    mainSource = ANHINGA_MISSILE_LAUNCHER_ROF_BONUS_PROPERTY,
                    ANHINGA_MISSILE_VELOCITY_BONUS_PROPERTY,
                    ANHINGA_MJD_REACTIVATION_DELAY_BONUS_PROPERTY,
                    ANHINGA_SHIP_INERTIA_BONUS_PROPERTY,
                )
            }
            else -> null
        }
    }
}


/**
 * A [DisplayedEffectSource] for displaying the value of a tactical mode property.
 */
private class TacticalModePropertyEffectSource<T: Any>(


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


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


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


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


    @Composable
    override fun valueOrMissingText(
        fit: Fit,
        affectingItemInfo: TacticalModeInfo,
        isCellDisplay: Boolean
    ): ValueOrMissing?{
        val tacticalMode = affectingItemInfo.tacticalMode

        val property = property(tacticalMode) ?: return null
        val value = property.value
        val valueText = formatValue(value, isCellDisplay)
        return valueText.valueOr(null)
    }


}


/**
 * The sources of tactical mode effects.
 */
private object TacticalModeEffectSources {



    /**
     * Returns an [Operation.POST_DIVIDE] [TacticalModePropertyEffectSource] for a [Double] property
     */
    private fun fromProperty(
        property: (TacticalMode) -> AttributeProperty<Double>?,
        operation: Operation,
        description: String,
        invertValue: Boolean = false
    ) = TacticalModePropertyEffectSource(
        property = property,
        description = description,
        formatValue = { value, isCellDisplay ->
            effectMagnitudeAsText(
                magnitude = if (invertValue) value.invert(operation) else value,
                operation = operation,
                isCellDisplay = isCellDisplay,
                absoluteValue = { _, _, _ -> error("$operation doesn't use absoluteValue") },
                withSign = isCellDisplay
            )
        }
    )


    /**
     * Returns an [Operation.POST_DIVIDE] [TacticalModePropertyEffectSource] for a [Double] property
     */
    private fun fromPostDivideProperty(
        property: (TacticalMode) -> AttributeProperty<Double>?,
        description: String,
        invertValue: Boolean = false
    ) = fromProperty(
        property = property,
        operation = Operation.POST_DIVIDE,
        description = description,
        invertValue = invertValue,
    )


    /**
     * Returns an [Operation.ADD_PERCENT] [TacticalModePropertyEffectSource] for a [Double] property
     */
    private fun fromAddPercentProperty(
        property: (TacticalMode) -> AttributeProperty<Double>?,
        description: String,
        invertValue: Boolean = false
    ) = fromProperty(
        property = property,
        operation = Operation.ADD_PERCENT,
        description = description,
        invertValue = invertValue,
    )


    /**
     * Bonus to some kind of resistances.
     */
    private fun resistanceBonusProperty(description: String) = fromPostDivideProperty(
        property = { mode ->
            mode.resonanceBonusDiv[DamageType.EM]  // Assume they're all the same
        },
        description = description,
        invertValue = true  // Convert resonance to resist
    )


    /**
     * Bonus to all shield resistances.
     */
    val SHIELD_RESISTANCE_BONUS_PROPERTY = resistanceBonusProperty(
        description = "bonus to all shield resistances"
    )


    /**
     * Bonus to all armor resistances.
     */
    val ARMOR_RESISTANCE_BONUS_PROPERTY = resistanceBonusProperty(
        description = "bonus to all armor resistances"
    )


    /**
     * Bonus to all shield and armor resistances.
     */
    val SHIELD_AND_ARMOR_RESISTANCE_BONUS_PROPERTY = resistanceBonusProperty(
        description = "bonus to all shield and armor resistances"
    )


    /**
     * Bonus to all armor and hull resistances.
     */
    val ARMOR_AND_HULL_RESISTANCE_BONUS_PROPERTY = resistanceBonusProperty(
        description = "bonus to all armor and hull resistances"
    )


    /**
     * Bonus to signature radius.
     */
    val SIGNATURE_RADIUS_BONUS_PROPERTY = fromPostDivideProperty(
        property = TacticalMode::signatureRadiusBonusDiv,
        description = "reduction in signature radius",
        invertValue = true
    )


    /**
     * Bonus to some kind of weapon damage.
     */
    private fun damageBonusProperty(description: String) = fromPostDivideProperty(
        property = TacticalMode::damageBonusDiv,
        description = description
    )


    /**
     * Bonus to rocket and light missile damage.
     */
    val ROCKET_AND_LIGHT_MISSILE_DAMAGE_BONUS_PROPERTY = damageBonusProperty(
        description = "bonus to Rocket and Light Missile damage",
    )


    /**
     * Bonus to small projectile turret damage.
     */
    val SMALL_PROJECTILE_TURRET_DAMAGE_BONUS_PROPERTY = damageBonusProperty(
        description = "bonus to small projectile turret damage"
    )


    /**
     * Bonus to small energy turret damage.
     */
    val SMALL_ENERGY_TURRET_DAMAGE_BONUS_PROPERTY = damageBonusProperty(
        description = "bonus to small energy turret damage"
    )


    /**
     * Bonus to small hybrid turret damage.
     */
    val SMALL_HYBRID_TURRET_DAMAGE_BONUS_PROPERTY = damageBonusProperty(
        description = "bonus to small hybrid turret damage"
    )


    /**
     * Bonus to weapon optimal range.
     */
    fun optimalRangeBonusProperty(description: String) = fromPostDivideProperty(
        property = TacticalMode::maxRangeBonusDiv,
        description = description
    )


    /**
     * Bonus to rocket and light missile velocity.
     */
    val ROCKET_AND_LIGHT_MISSILE_VELOCITY_BONUS_PROPERTY = optimalRangeBonusProperty(
        description = "bonus to rocket and light Missile velocity"
    )


    /**
     * Bonus to small energy turret optimal range.
     */
    val SMALL_ENERGY_TURRET_OPTIMAL_RANGE_BONUS_PROPERTY = optimalRangeBonusProperty(
        description = "bonus to small energy turret optimal range"
    )


    /**
     * Bonus to small hybrid turret optimal range.
     */
    val SMALL_HYBRID_TURRET_OPTIMAL_RANGE_BONUS_PROPERTY = optimalRangeBonusProperty(
        description = "bonus to small hybrid turret optimal range"
    )


    /**
     * Bonus to small projectile turret tracking range.
     */
    val SMALL_PROJECTILE_TURRET_TRACKING_BONUS_PROPERTY = fromPostDivideProperty(
        property = TacticalMode::trackingBonusDiv,
        description = "bonus to small projectile turret tracking"
    )


    /**
     * Bonus to sensor strength.
     */
    val SENSOR_STRENGTH_BONUS_PROPERTY = fromPostDivideProperty(
        property = { mode -> mode.sensorStrengthBonusDiv.values.maxBy { it?.value ?: 0.0 } },
        description = "bonus to sensor strength"
    )


    /**
     * Bonus to targeting range.
     */
    val TARGETING_RANGE_BONUS_PROPERTY = fromPostDivideProperty(
        property = TacticalMode::targetingRangeBonusDiv,
        description = "bonus to targeting range"
    )


    /**
     * Bonus to resistance to Sensor Dampeners and Weapon Disruptors.
     */
    val EWAR_RESISTANCE_BONUS_PROPERTY = fromPostDivideProperty(
        property = TacticalMode::ewarResistanceBonusDiv,
        description = "bonus to ewar resistance",
        invertValue = true
    )


    /**
     * Bonus to velocity, or AB/MWD speed boost.
     */
    private fun velocityProperty(description: String) = fromPostDivideProperty(
        property = TacticalMode::velocityBonusDiv,
        description = description,
    )


    /**
     * Bonus to ship maximum velocity.
     */
    val MAX_VELOCITY_BONUS_PROPERTY = velocityProperty(
        description = "bonus to ship maximum velocity"
    )


    /**
     * Bonus to Afterburner and Microwarpdrive speed boost.
     */
    val AFTERBURNER_AND_MICROWARPDRIVE_SPEED_BOOST_BONUS_PROPERTY = velocityProperty(
        description = "bonus to AB and MWD speed boost"
    )


    /**
     * Bonus to Microwarpdrive speed boost.
     */
    val MICROWARPDRIVE_SPEED_BOOST_BONUS_PROPERTY = velocityProperty(
        description = "bonus to MWD speed boost"
    )


    /**
     * Bonus to agility.
     */
    val INERTIA_MODIFIER_BONUS_PROPERTY = fromPostDivideProperty(
        property = TacticalMode::agilityBonusDiv,
        description = "bonus to agility",
        invertValue = true
    )


    /**
     * Reduction in Microwarpdrive signature radius bloom.
     */
    val MICROWARPDRIVE_SIGNATURE_RADIUS_BONUS_PROPERTY = fromPostDivideProperty(
        property = TacticalMode::microwarpdriveSignaturePenaltyBonusDiv,
        description = "reduction in MWD signature radius penalty",
        invertValue = true
    )


    /**
     * Bonus to armor repairer duration.
     */
    val ARMOR_REPAIRER_DURATION_BONUS_PROPERTY = fromPostDivideProperty(
        property = TacticalMode::armorRepairerDurationBonusDiv,
        description = "reduction in armor repairer duration",
        invertValue = true
    )


    /**
     * Bonus to Microwarpdrive capacitor need.
     */
    val MICROWARPDRIVE_CAPACITOR_NEED_BONUS_PROPERTY = fromPostDivideProperty(
        property = TacticalMode::microwarpdriveCapacitorBonusDiv,
        description = "reduction in MWD capacitor need",
        invertValue = true
    )


    /**
     * Bonus to remote shield booster amount.
     */
    val REMOTE_SHIELD_BOOSTER_AMOUNT_BONUS_PROPERTY = fromPostDivideProperty(
        property = TacticalMode::remoteRepairAmountDiv,
        description = "bonus to remote shield booster repair amount",
    )


    /**
     * Bonus to remote armor repairer amount.
     */
    val REMOTE_ARMOR_REPAIRER_AMOUNT_BONUS_PROPERTY = fromPostDivideProperty(
        property = TacticalMode::remoteRepairAmountDiv,
        description = "bonus to remote armor repairer repair amount",
    )


    /**
     * Bonus to remote shield booster capacitor need.
     */
    val REMOTE_SHIELD_BOOSTER_CAPACITOR_NEED_BONUS_PROPERTY = fromPostDivideProperty(
        property = TacticalMode::remoteRepairCapacitorCostDiv,
        description = "reduction in remote shield booster capacitor need",
        invertValue = true
    )


    /**
     * Bonus to remote armor repairer capacitor need.
     */
    val REMOTE_ARMOR_REPAIRER_CAPACITOR_NEED_BONUS_PROPERTY = fromPostDivideProperty(
        property = TacticalMode::remoteRepairCapacitorCostDiv,
        description = "reduction in remote armor repairer capacitor need",
        invertValue = true
    )


    /**
     * Bonus to shield recharge time.
     */
    val SHIELD_RECHARGE_TIME_BONUS_PROPERTY = fromPostDivideProperty(
        property = TacticalMode::shieldRechargeBonusDiv,
        description = "reduction in shield recharge time",
        invertValue = true
    )


    /**
     * Bonus to light missile velocity for the Skua.
     */
    val SKUA_LIGHT_MISSILE_VELOCITY_BONUS_PROPERTY = fromAddPercentProperty(
        property = TacticalMode::skuaLightMissileVelocityBonus,
        description = "bonus to light missile velocity",
    )


    /**
     * Bonus to light missile velocity for the Skua.
     */
    val SKUA_ROCKET_VELOCITY_BONUS_PROPERTY = fromAddPercentProperty(
        property = TacticalMode::skuaRocketVelocityBonus,
        description = "bonus to rocket velocity",
    )


    /**
     * Bonus to missile launcher RoF for the Anhinga.
     */
    val ANHINGA_MISSILE_LAUNCHER_ROF_BONUS_PROPERTY = fromAddPercentProperty(
        property = TacticalMode::anhingaMissileRoFBonus,
        description = "bonus to missile launcher rate of fire",
        invertValue = true
    )


    /**
     * Bonus to missile velocity for the Anhinga.
     */
    val ANHINGA_MISSILE_VELOCITY_BONUS_PROPERTY = fromAddPercentProperty(
        property = TacticalMode::anhingaMissileVelocityBonus,
        description = "bonus in missile velocity",
    )


    /**
     * Reduction in missile velocity for the Anhinga.
     */
    val ANHINGA_MISSILE_VELOCITY_PENALTY_PROPERTY = fromAddPercentProperty(
        property = TacticalMode::anhingaMissileVelocityReduction,
        description = "reduction in missile velocity",
        invertValue = true
    )


    /**
     * Bonus to missile flight time for the Anhinga.
     */
    val ANHINGA_MISSILE_FLIGHT_TIME_BONUS_PROPERTY = fromAddPercentProperty(
        property = TacticalMode::anhingaMissileFlightTimeBonus,
        description = "bonus to missile flight time",
    )


    /**
     * Penalty to missile flight time for the Anhinga.
     */
    val ANHINGA_MISSILE_FLIGHT_TIME_PENALTY_PROPERTY = fromAddPercentProperty(
        property = TacticalMode::anhingaMissileFlightTimeBonus,
        description = "decrease in missile flight time",
        invertValue = true
    )


    /**
     * Bonus to MJD reactivation delay for the Anhinga.
     */
    val ANHINGA_MJD_REACTIVATION_DELAY_BONUS_PROPERTY = fromAddPercentProperty(
        property = TacticalMode::anhingaMjdReactivationDelayBonus,
        description = "reduction in MJD reactivation delay",
        invertValue = true
    )


    /**
     * Bonus to ship inertia for the Anhinga.
     */
    val ANHINGA_SHIP_INERTIA_BONUS_PROPERTY = fromAddPercentProperty(
        property = TacticalMode::anhingaShipInertiaBonus,
        description = "bonus to ship inertia",
        invertValue = true
    )


}
