package theorycrafter.fitting

import eve.data.AttributeModifier
import eve.data.Effect
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue


/**
 * Tests fitting functionality related to command effects.
 */
class CommandEffectsTest {


    /**
     * Tests a command effect on the ship, applied via [WarfareBuffs].
     */
    @Test
    fun testCommandBurstEffect() = runFittingTest {
        val targetAttribute = attribute()

        val (commandBurstModuleType, chargeType) = setupCommandBurst(
            buffId = 1,
            buffValue = 2.0,
            warfareBuffEffectAndDefaultValue = { warfareBuffValueAttribute ->
                Pair(
                    effectOnShip(
                        category = Effect.Category.ALWAYS,
                        modifyingAttribute = warfareBuffValueAttribute,
                        modifiedAttribute = targetAttribute,
                        operation = AttributeModifier.Operation.POST_MULTIPLY
                    ),
                    1.0
                )
            }
        )

        val shipType = testShipType {
            attributeValue(targetAttribute, 50.0)
        }

        val (fit, _) = fit(shipType)
        modify {
            val commandBurst = fit.fitModule(commandBurstModuleType, 0)
            commandBurst.setCharge(chargeType)
            commandBurst.setState(Module.State.ACTIVE)
        }

        fit.ship.assertPropertyEquals(
            attribute = targetAttribute,
            expected = 100.0,
            message = "Command Burst effect to ship applied incorrectly"
        )
    }


    /**
     * Tests that when two command bursts affect the same attribute, only the one with the maximum effect
     * (modifyingAttribute value) is applied.
     */
    @Test
    fun testCommandBurstAppliesMaxEffect() = runFittingTest {
        val warfareBuffValueAttribute = attribute()
        val targetAttribute = attribute()

        fun commandBurstAndCharge(buffValue: Double, addWarfareBuffsEffect: Boolean) = setupCommandBurst(
            buffId = 1,
            buffValue = buffValue,
            warfareBuffValueAttribute = warfareBuffValueAttribute,
            warfareBuffEffectAndDefaultValue =
            if (addWarfareBuffsEffect) { warfareBuffValueAttribute ->
                Pair(
                    effectOnShip(
                        category = Effect.Category.ALWAYS,
                        modifyingAttribute = warfareBuffValueAttribute,
                        modifiedAttribute = targetAttribute,
                        operation = AttributeModifier.Operation.POST_MULTIPLY
                    ),
                    1.0
                )
            }
            else null
        )

        val (commandBurstModuleType1, chargeType1) =
            commandBurstAndCharge(buffValue = 2.0, addWarfareBuffsEffect = true)
        val (commandBurstModuleType2, chargeType2) =
            commandBurstAndCharge(buffValue = 3.0, addWarfareBuffsEffect = false)

        val shipType = testShipType {
            attributeValue(targetAttribute, 50.0)
        }

        val (fit, modules) = fit(shipType, commandBurstModuleType1, commandBurstModuleType2)
        val (module1, module2) = modules
        modify {
            module1.setCharge(chargeType1)
            module1.setState(Module.State.ACTIVE)
            module2.setCharge(chargeType2)
            module2.setState(Module.State.ACTIVE)
        }

        fit.ship.assertPropertyEquals(
            attribute = targetAttribute,
            expected = 150.0,  // 50*3
            message = "Two Command Bursts effects to a ship applied incorrectly"
        )

        // Check that only the module with the greater effect has an applied effect magnitude
        assertTrue { module1.appliedEffects.all { it.magnitude == null } }
        assertEquals(expected = 3.0, actual = module2.appliedEffects.first().magnitude)
    }


    /**
     * Tests a command effect on another fit.
     */
    @Test
    fun testCommandBurstEffectOnAnotherFit() = runFittingTest {
        val targetAttribute = attribute()

        val (commandBurstModuleType, chargeType) = setupCommandBurst(
            buffId = 1,
            buffValue = 2.0,
            warfareBuffEffectAndDefaultValue = { warfareBuffValueAttribute ->
                Pair(
                    effectOnShip(
                        category = Effect.Category.ALWAYS,
                        modifyingAttribute = warfareBuffValueAttribute,
                        modifiedAttribute = targetAttribute,
                        operation = AttributeModifier.Operation.POST_MULTIPLY
                    ),
                    1.0
                )
            }
        )

        val shipType = testShipType {
            attributeValue(targetAttribute, 50.0)
        }

        // Create commanding fit
        val (commandingFit, _) = fit(shipType)
        modify {
            val commandBurst = commandingFit.fitModule(commandBurstModuleType, 0)
            commandBurst.setCharge(chargeType)
            commandBurst.setState(Module.State.ACTIVE)
        }

        // Add commanded fit and set up its commanding fit
        val (commandedFit, _) = fit(shipType)
        val commandEffect = modify {
            commandedFit.addCommandEffect(commandingFit)
        }
        commandedFit.ship.assertPropertyEquals(
            attribute = targetAttribute,
            expected = 100.0,
            message = "Command Burst effect to ship of another fit applied incorrectly"
        )

        // Disable the command effect and check the property value
        modify {
            commandEffect.setEnabled(false)
        }
        commandedFit.ship.assertPropertyEquals(
            attribute = targetAttribute,
            expected = 50.0,
            message = "Command Burst effect to ship of another fit applied even when disabled"
        )

        // Enable the command effect and check the property value
        modify {
            commandEffect.setEnabled(true)
        }
        commandedFit.ship.assertPropertyEquals(
            attribute = targetAttribute,
            expected = 100.0,
            message = "Command Burst effect to ship of another fit applied incorrectly after being re-enabled"
        )

        // Remove the command effect and check the property value
        modify {
            commandedFit.removeCommandEffect(commandEffect)
        }
        commandedFit.ship.assertPropertyEquals(
            attribute = targetAttribute,
            expected = 50.0,
            message = "Command Burst effect to ship of another fit still applied after being removed"
        )
    }


    /**
     * Tests that when two command bursts affect the same attribute are applied from different ships, only the one with
     * the maximum effect (modifyingAttribute value) is applied.
     */
    @Test
    fun testCommandBurstFromTwoFitsAppliesMaxEffect() = runFittingTest {
        val warfareBuffValueAttribute = attribute()
        val targetAttribute = attribute()

        fun commandBurstAndCharge(buffValue: Double, addWarfareBuffsEffect: Boolean) = setupCommandBurst(
            buffId = 1,
            buffValue = buffValue,
            warfareBuffValueAttribute = warfareBuffValueAttribute,
            warfareBuffEffectAndDefaultValue =
            if (addWarfareBuffsEffect) { warfareBuffValueAttribute ->
                Pair(
                    effectOnShip(
                        category = Effect.Category.ALWAYS,
                        modifyingAttribute = warfareBuffValueAttribute,
                        modifiedAttribute = targetAttribute,
                        operation = AttributeModifier.Operation.POST_MULTIPLY
                    ),
                    1.0
                )
            }
            else null
        )

        val (commandBurstModuleType1, chargeType1) =
            commandBurstAndCharge(buffValue = 2.0, addWarfareBuffsEffect = true)
        val (commandBurstModuleType2, chargeType2) =
            commandBurstAndCharge(buffValue = 3.0, addWarfareBuffsEffect = false)

        val shipType = testShipType {
            attributeValue(targetAttribute, 50.0)
        }

        val (commandingFit1, _) = fit(shipType)
        modify {
            val commandBurst = commandingFit1.fitModule(commandBurstModuleType1, 0)
            commandBurst.setCharge(chargeType1)
            commandBurst.setState(Module.State.ACTIVE)
        }

        val (commandingFit2, _) = fit(shipType)
        modify {
            val commandBurst = commandingFit2.fitModule(commandBurstModuleType2, 0)
            commandBurst.setCharge(chargeType2)
            commandBurst.setState(Module.State.ACTIVE)
        }

        val (commandedFit, _) = fit(shipType)
        modify {
            commandedFit.addCommandEffect(commandingFit1)
            commandedFit.addCommandEffect(commandingFit2)
        }

        commandedFit.ship.assertPropertyEquals(
            attribute = targetAttribute,
            expected = 150.0,  // 50*3
            message = "Two Command Bursts effects from two commanding fits to a ship applied incorrectly"
        )
    }


    /**
     * Tests adding and removing a command effect.
     */
    @Test
    fun testRemoveCommandEffect() = runFittingTest {
        val targetAttribute = attribute()

        val (commandBurstModuleType, chargeType) = setupCommandBurst(
            buffId = 1,
            buffValue = 2.0,
            warfareBuffEffectAndDefaultValue = { warfareBuffValueAttribute ->
                Pair(
                    effectOnShip(
                        category = Effect.Category.ALWAYS,
                        modifyingAttribute = warfareBuffValueAttribute,
                        modifiedAttribute = targetAttribute,
                        operation = AttributeModifier.Operation.POST_MULTIPLY
                    ),
                    1.0
                )
            }
        )

        val shipType = testShipType {
            attributeValue(targetAttribute, 50.0)
        }

        // Create commanding fit
        val (commandingFit, _) = fit(shipType)
        modify {
            val commandBurst1 = commandingFit.fitModule(commandBurstModuleType, 0)
            commandBurst1.setCharge(chargeType)
            commandBurst1.setState(Module.State.ACTIVE)

            // There was a crash when removing a command effect with two command bursts, so test that
            val commandBurst2 = commandingFit.fitModule(commandBurstModuleType, 1)
            commandBurst2.setCharge(chargeType)
            commandBurst2.setState(Module.State.ACTIVE)
        }

        // Add commanded fit and set up its commanding fit
        val (commandedFit, _) = fit(shipType)
        val commandEffect = modify {
            commandedFit.addCommandEffect(commandingFit)
        }
        commandedFit.ship.assertPropertyEquals(
            attribute = targetAttribute,
            expected = 100.0,
            message = "Command Burst effect to ship of another fit applied incorrectly"
        )

        modify {
            commandedFit.removeCommandEffect(commandEffect)
        }

        commandedFit.ship.assertPropertyEquals(
            attribute = targetAttribute,
            expected = 50.0,
            message = "Command Burst effect to ship of another fit still applied after removing the command effect"
        )
    }


}