package theorycrafter.tournaments.impl

import eve.data.*
import eve.data.typeid.*
import theorycrafter.fitting.Fit
import theorycrafter.tournaments.*
import theorycrafter.tournaments.TournamentRules.FittingRules
import theorycrafter.ui.fiteditor.*
import theorycrafter.utils.with


/**
 * The descriptor for Anger Games 6 (2024).
 */
val AngerGames6 = TournamentDescriptor(
    id = "ag6",
    name = "Anger Games 6",
    shortName = "AG6",
    rulesFactory = ::angerGames6Rules,
    winner = "No Spaceships for Old Men"
)


/**
 * Maps ship type names to their point values.
 */
private val ShipCostByName = mapOf(
    "Enyo" to 7,
    "Harpy" to 7,
    "Hawk" to 7,
    "Ishkur" to 7,
    "Jaguar" to 7,
    "Retribution" to 7,
    "Vengeance" to 9,
    "Wolf" to 7,
    "Nergal" to 9,
    "Naga" to 23,
    "Oracle" to 23,
    "Talos" to 23,
    "Tornado" to 23,
    "Abaddon" to 41,
    "Apocalypse" to 38,
    "Armageddon" to 41,
    "Dominix" to 38,
    "Hyperion" to 41,
    "Maelstrom" to 38,
    "Megathron" to 38,
    "Praxis" to 38,
    "Raven" to 38,
    "Rokh" to 38,
    "Scorpion" to 41,
    "Tempest" to 38,
    "Typhoon" to 38,
    "Apocalypse Navy Issue" to 47,
    "Armageddon Navy Issue" to 47,
    "Dominix Navy Issue" to 47,
    "Megathron Navy Issue" to 47,
    "Raven Navy Issue" to 47,
    "Scorpion Navy Issue" to 47,
    "Tempest Fleet Issue" to 47,
    "Typhoon Fleet Issue" to 47,
    "Barghest" to 53,
    "Bhaalgorn" to 53,
    "Machariel" to 48,
    "Nightmare" to 53,
    "Rattlesnake" to 53,
    "Vindicator" to 53,
    "Leshak" to 53,
    "Panther" to 45,
    "Redeemer" to 45,
    "Sin" to 45,
    "Widow" to 51,
    "Brutix" to 27,
    "Cyclone" to 27,
    "Drake" to 27,
    "Ferox" to 27,
    "Gnosis" to 27,
    "Harbinger" to 27,
    "Hurricane" to 27,
    "Myrmidon" to 27,
    "Prophecy" to 27,
    "Drekavac" to 34,
    "Brutix Navy Issue" to 33,
    "Drake Navy Issue" to 33,
    "Harbinger Navy Issue" to 33,
    "Hurricane Fleet Issue" to 33,
    "Cyclone Fleet Issue" to 33,
    "Ferox Navy Issue" to 33,
    "Myrmidon Navy Issue" to 33,
    "Prophecy Navy Issue" to 33,
    "Alligator" to 35,
    "Khizriel" to 33,
    "Bifrost" to 12,
    "Magus" to 12,
    "Pontifex" to 12,
    "Stork" to 12,
    "Draugur" to 15,
    "Absolution" to 37,
    "Astarte" to 37,
    "Claymore" to 37,
    "Damnation" to 37,
    "Eos" to 42,
    "Nighthawk" to 37,
    "Sleipnir" to 37,
    "Vulture" to 37,
    "Anathema" to 3,
    "Buzzard" to 3,
    "Cheetah" to 3,
    "Helios" to 3,
    "Hydra" to 3,
    "Caracal" to 9,
    "Maller" to 10,
    "Moa" to 8,
    "Omen" to 8,
    "Rupture" to 8,
    "Stabber" to 8,
    "Thorax" to 8,
    "Vexor" to 10,
    "Vedmak" to 19,
    "Augoror Navy Issue" to 18,
    "Caracal Navy Issue" to 16,
    "Exequror Navy Issue" to 19,
    "Omen Navy Issue" to 16,
    "Osprey Navy Issue" to 16,
    "Scythe Fleet Issue" to 16,
    "Stabber Fleet Issue" to 16,
    "Vexor Navy Issue" to 16,
    "Ashimmu" to 19,
    "Cynabal" to 16,
    "Gila" to 21,
    "Orthrus" to 19,
    "Phantasm" to 19,
    "Stratios" to 19,
    "Vigilant" to 19,
    "Algos" to 5,
    "Catalyst" to 5,
    "Coercer" to 5,
    "Corax" to 5,
    "Cormorant" to 5,
    "Dragoon" to 5,
    "Sunesis" to 5,
    "Talwar" to 5,
    "Thrasher" to 5,
    "Hyena" to 12,
    "Keres" to 14,
    "Kitsune" to 12,
    "Sentinel" to 14,
    "Damavik" to 8,
    "Atron" to 4,
    "Breacher" to 4,
    "Condor" to 4,
    "Executioner" to 4,
    "Heron" to 4,
    "Imicus" to 4,
    "Incursus" to 4,
    "Kestrel" to 4,
    "Magnate" to 4,
    "Merlin" to 4,
    "Probe" to 4,
    "Rifter" to 4,
    "Slasher" to 4,
    "Tormentor" to 4,
    "Tristan" to 4,
    "Venture" to 4,
    "Metamorphosis" to 4,
    "Caldari Navy Hookbill" to 5,
    "Crucifier Navy Issue" to 7,
    "Federation Navy Comet" to 5,
    "Griffin Navy Issue" to 5,
    "Imperial Navy Slicer" to 5,
    "Maulus Navy Issue" to 5,
    "Republic Fleet Firetail" to 5,
    "Vigil Fleet Issue" to 5,
    "Astero" to 6,
    "Cruor" to 7,
    "Daredevil" to 6,
    "Dramiel" to 6,
    "Garmur" to 6,
    "Succubus" to 6,
    "Worm" to 8,
    "Cerberus" to 25,
    "Deimos" to 25,
    "Eagle" to 25,
    "Ishtar" to 25,
    "Muninn" to 25,
    "Sacrilege" to 25,
    "Vagabond" to 25,
    "Zealot" to 25,
    "Ikitursa" to 27,
    "Broadsword" to 22,
    "Devoter" to 22,
    "Onyx" to 22,
    "Phobos" to 22,
    "Ares" to 5,
    "Claw" to 5,
    "Crow" to 5,
    "Crusader" to 5,
    "Malediction" to 5,
    "Raptor" to 5,
    "Stiletto" to 5,
    "Taranis" to 5,
    "Eris" to 7,
    "Flycatcher" to 7,
    "Heretic" to 7,
    "Sabre" to 7,
    "Basilisk" to 33,
    "Guardian" to 33,
    "Oneiros" to 33,
    "Scimitar" to 33,
    "Zarmazd" to 38,
    "Deacon" to 13,
    "Kirin" to 13,
    "Scalpel" to 13,
    "Thalia" to 13,
    "Golem" to 47,
    "Kronos" to 47,
    "Paladin" to 47,
    "Vargur" to 47,
    "Catalyst Navy Issue" to 8,
    "Coercer Navy Issue" to 8,
    "Cormorant Navy Issue" to 8,
    "Thrasher Fleet Issue" to 8,
    "Mekubal" to 8,
    "Mamba" to 8,
    "Kikimora" to 10,
    "Punisher" to 6,
    "Arazu" to 28,
    "Curse" to 31,
    "Falcon" to 25,
    "Huginn" to 25,
    "Lachesis" to 28,
    "Pilgrim" to 28,
    "Rapier" to 25,
    "Rook" to 28,
    "Tiamat" to 25,
    "Ibis" to 1,
    "Impairor" to 1,
    "Reaper" to 1,
    "Velator" to 1,
    "Skybreaker" to 6,
    "Hound" to 5,
    "Manticore" to 5,
    "Nemesis" to 5,
    "Purifier" to 5,
    "Stormbringer" to 13,
    "Legion" to 30,
    "Loki" to 32,
    "Proteus" to 30,
    "Tengu" to 30,
    "Confessor" to 9,
    "Hecate" to 9,
    "Jackdaw" to 9,
    "Svipul" to 9,
    "Arbitrator" to 15,
    "Bellicose" to 8,
    "Blackbird" to 16,
    "Celestis" to 15,
    "Crucifier" to 9,
    "Griffin" to 6,
    "Maulus" to 8,
    "Vigil" to 6,
    "Badger" to 3,
    "Bestower" to 3,
    "Epithal" to 3,
    "Hoarder" to 3,
    "Iteron Mark V" to 3,
    "Kryos" to 3,
    "Mammoth" to 3,
    "Miasmos" to 3,
    "Nereus" to 3,
    "Primae" to 3,
    "Sigil" to 3,
    "Tayra" to 3,
    "Wreathe" to 3,
    "Rodiva" to 26,
    "Augoror" to 22,
    "Exequror" to 22,
    "Osprey" to 22,
    "Scythe" to 22,
    "Bantam" to 5,
    "Burst" to 5,
    "Inquisitor" to 5,
    "Navitas" to 5,
    "Thunderchild" to 35,
    "Magnate Navy Issue" to 5,
    "Heron Navy Issue" to 5,
    "Imicus Navy Issue" to 5,
    "Probe Fleet Issue" to 5,
)


/**
 * The composition rules for Anger Games 6.
 */
private class Ag6CompositionRules(eveData: EveData): BasicPointsCompositionRules(
    eveData = eveData,
    maxCompositionSize = 7,
    maxCompositionCost = 150,
    shipCostByType = ShipCostByName.mapKeys { (name, _) -> eveData.shipType(name) },
) {

    override fun compositionShipsIllegalityReason(composition: Composition): List<String?> {
        with(eveData) {
            return shipIllegalityBySizeClass(
                composition = composition,
                maxInSizeClass = {
                    when (it) {
                        ShipSizeClass.LOGI_CRUISER -> 1
                        ShipSizeClass.LOGI_FRIGATE -> 2
                        else -> 3
                    }
                },
                ::standardLogiLegalityCheck
            )
        }
    }

}


/**
 * The fitting rules for Anger Games 6.
 */
private class Ag6FittingRules(
    private val eveData: EveData
): FittingRules {


    override fun isModuleLegal(moduleType: ModuleType, shipType: ShipType, isFlagship: Boolean): Boolean {
        with(eveData) {
            if (!moduleType.isTech1Item && !moduleType.isTech2Item)
                return false

            return when {
                moduleType.isRemoteRepairer -> shipType.isLogistics
                moduleType.isRemoteCapacitorTransmitter() -> isRemoteCapacitorTransmitterLegal(shipType)
                moduleType.isEcm() || moduleType.isBurstJammer() -> isEcmLegal(moduleType, shipType)
                moduleType.isWeaponDisruptor() -> isWeaponDisruptorLegal(moduleType, shipType)
                moduleType.isRemoteSensorDampener() -> isRemoteSensorDampenerLegal(moduleType, shipType)
                moduleType.isMicroJumpFieldGenerator() -> false
                moduleType.isBastionModule() -> false
                moduleType.isCloakingDevice() -> false
                moduleType.isRig -> moduleType.isTech1Item
                else -> true
            }
        }
    }


    override fun fitModulesLegality(moduleTypes: List<ModuleType>, shipType: ShipType, isFlagship: Boolean): List<String?> {
        with(eveData) {
            val regularIllegality = moduleTypes.map {
                if (isModuleLegal(it, shipType, isFlagship)) null else "Module is illegal here"
            }
            val illegalityByGroup = moduleIllegalityByGroup(
                moduleTypes = moduleTypes,
                maxInGroup = {
                    when (it) {
                        groups.armorPlate -> if (shipType.isBattleship) 1 else Unlimited
                        groups.shieldExtender -> if (shipType.isBattleship) 2 else Unlimited
                        groups.remoteCapacitorTransmitter -> 1
                        groups.ancillaryShieldBooster -> 1
                        groups.warpDisruptFieldGenerator -> 1
                        else -> Unlimited
                    }
                },
                additionalChecks = arrayOf(
                    { group, countByGroup ->
                        if (shipType.isBattleship)
                            standardBattleshipBufferTankCheck(group, countByGroup)
                        else
                            null
                    }
                )
            )

            return combineIllegalityReasons(regularIllegality, illegalityByGroup)
        }
    }


    override fun isChargeLegal(chargeType: ChargeType?, moduleType: ModuleType): Boolean {
        with(eveData) {
            if (moduleType.isWarpDisruptionFieldGenerator())
                return (chargeType != null) && chargeType.isFocusedWarpScramblingScript
            if (moduleType.isInterdictionSphereLauncher())
                return (chargeType != null) && chargeType.isStasisWebificationProbe
            return isChargeLegal(chargeType)
        }
    }


    override fun isDroneLegal(droneType: DroneType): Boolean {
        return with(eveData) {
            when {
                droneType.name == "Gecko" -> false
                droneType.isSentry -> droneType.isTech1Item
                droneType.isCombatDrone() -> droneType.isTech1Item || droneType.isEmpireNavyFactionItem
                droneType.isShieldMaintenanceBot() ||
                        droneType.isArmorMaintenanceBot() ||
                        droneType.isHullMaintenanceBot() -> droneType.isTech1Item
                droneType.isStasisWebifyingDrone() ||
                        droneType.isTargetPaintingDrone() ||
                        droneType.isEnergyNeutralizerDrone() ||
                        droneType.isEcmDrone() ||
                        droneType.isSensorDampeningDrone() ||
                        droneType.isTrackingDisruptingDrone() -> true
                else -> false
            }
        }
    }


    override fun isImplantLegal(implantType: ImplantType): Boolean {
        return implantType.is010203Hardwiring
    }


    override fun isCargoItemLegal(itemType: EveItemType): Boolean {
        with(eveData) {
            if (itemType.isCargoContainer)
                return false

            return when (itemType) {
                is ChargeType -> isChargeLegal(itemType)
                is ImplantType -> isImplantLegal(itemType)
                is BoosterType -> isBoosterLegal(itemType)
                else -> true
            }
        }
    }


    override fun preloadedCharge(
        fit: Fit,
        moduleType: ModuleType,
        charges: Collection<ChargeType>
    ): Pair<Boolean, ChargeType?> {
        with(eveData) {
            val chargeType = when {
                moduleType.isEnergyWeapon() -> forEnergyTurret(fit.ship.type, charges)
                moduleType.isHybridWeapon() -> forHybridTurret(fit.ship.type, charges)
                moduleType.isProjectileWeapon() -> forProjectileTurret(charges)
                moduleType.isMissileLauncher() -> forMissileLauncher(fit, moduleType, charges)
                moduleType.isWarpDisruptionFieldGenerator() -> charges.find { it.isFocusedWarpScramblingScript }
                else -> return Pair(false, null)
            }

            return Pair(true, chargeType)
        }
    }


    override fun hasPreloadedCharge(
        fit: Fit,
        moduleType: ModuleType,
        charges: Collection<ChargeType>,
    ): Pair<Boolean, Boolean> {
        val hasTournamentSpecificCharge = with(eveData, moduleType) {
            isEnergyWeapon() ||
                    isHybridWeapon() ||
                    isProjectileWeapon() ||
                    isMissileLauncher() ||
                    isWarpDisruptionFieldGenerator()
        }

        return Pair(hasTournamentSpecificCharge, true)
    }


    override val packForBattleConfiguration: PackForBattleConfiguration
        get() = PackForBattleConfig


}


/**
 * The rules for Anger Games 6.
 */
private fun angerGames6Rules(eveData: EveData) =
    TournamentRules(
        compositionRules = Ag6CompositionRules(eveData),
        fittingRules = Ag6FittingRules(eveData)
    )



/**
 * Returns whether the given remote capacitor transmitter module is legal on the given ship type.
 */
context(EveData)
private fun isRemoteCapacitorTransmitterLegal(shipType: ShipType): Boolean {
    return (shipType.group == groups.logistics) || (shipType.name in Tech1LogiCruisers)
}


/**
 * Returns whether the given ECM module is legal on the given ship type.
 */
context(EveData)
private fun isEcmLegal(moduleType: ModuleType, shipType: ShipType): Boolean {
    return moduleType.isTech1Item && (shipType.name in EcmShips)
}


/**
 * Returns whether the given weapon disruption module is legal on the given ship type.
 */
context(EveData)
private fun isWeaponDisruptorLegal(moduleType: ModuleType, shipType: ShipType): Boolean {
    return moduleType.isTech1Item && (shipType.name in WeaponDisruptionShips)
}


/**
 * Returns whether the given sensor dampener module is legal on the given ship type.
 */
context(EveData)
private fun isRemoteSensorDampenerLegal(moduleType: ModuleType, shipType: ShipType): Boolean {
    return moduleType.isTech1Item && (shipType.name in SensorDampeningShips)
}


/**
 * Returns whether the charge is legal, whether in a module or in the cargohold.
 */
context(EveData)
private fun isChargeLegal(chargeType: ChargeType?): Boolean {
    return when {
        chargeType == null -> true
        chargeType.isWarpDisruptionScript() -> chargeType.isFocusedWarpScramblingScript
        chargeType.isInterdictionSphereLauncherProbe() -> chargeType.isStasisWebificationProbe
        chargeType.isAmmo() -> when {
            chargeType.isTech2Item -> true
            chargeType.isBomb() -> true
            chargeType.isDefenderMissile() -> true
            chargeType.isAutoTargetingMissile() -> chargeType.isEmpireNavyFactionItem
            chargeType.isMissile()
                || chargeType.isProjectileAmmo()
                || chargeType.isFrequencyCrystalAmmo()
                || chargeType.isHybridChargeAmmo() -> chargeType.isElitePirateFactionItem
            else -> true
        }
        else -> true
    }
}


/**
 * Returns the crystal to preload into the given laser turret.
 */
private fun EveData.forEnergyTurret(shipType: ShipType, crystals: Collection<ChargeType>): ChargeType? {
    // Advanced pulse crystal with the longest range should be Scorch
    val scorch = crystals
        .filter { it.group == groups.advancedPulseLaserCrystal }
        .maxByOrNull { it.attributeValueOrNull(attributes.weaponRangeMultiplier) ?: 0.0 }
    if (scorch != null)
        return scorch

    // Highest damage faction should be Multifrequency
    return crystals
        .filter { isElitePirateCrystalProperForShipType(shipType, it) }
        .consistentMaxByOrNull { it.totalDamage ?: 0.0 }
}


/**
 * Returns the hybrid charge to preload into the given hybrid turret.
 */
private fun EveData.forHybridTurret(shipType: ShipType, charges: Collection<ChargeType>): ChargeType? {
    return charges
        .filter { isElitePirateHybridChargeProperForShipType(shipType, it) }
        .consistentMaxByOrNull {
            it.totalDamage ?: 0.0
        }
}


/**
 * Returns the ammo to preload into the given projectile turret.
 */
private fun EveData.forProjectileTurret(ammo: Collection<ChargeType>): ChargeType?{
    // Advanced autocannon ammo with the highest damage should be Hail
    val hail = ammo
        .filter { it.group == groups.advancedAutocannonAmmo }
        .maxByOrNull { it.totalDamage ?: 0.0 }
    if (hail != null)
        return hail

    // EMP, Phased Plasma and Fusion all have the same damage, but Phased Plasma is the most versatile one.
    return ammo
        .filter { it.isElitePirateFactionItem }
        .consistentMaxByOrNull(preferNameContains = "Phased Plasma") { it.totalDamage ?: 0.0 }
}


/**
 * Returns the missile to preload into the given missile launcher fitted to the given ship.
 */
private fun EveData.forMissileLauncher(
    fit: Fit,
    missileLauncher: ModuleType,
    missiles: Collection<ChargeType>
): ChargeType? {
    val dreadGuristas = missiles.filter { it.isElitePirateFactionItem }
    return preferredMissile(fit, missileLauncher, dreadGuristas)
}


/**
 * The configuration for the "Pack for Battle" dialog.
 */
private val PackForBattleConfig = object: PackForBattleConfiguration {


    context(EveData)
    override fun script(chargeType: ChargeType): Boolean {
        return isChargeLegal(chargeType)
    }


    context(EveData)
    override fun interdictionSphereLauncherProbe(chargeType: ChargeType): Boolean {
        return isChargeLegal(chargeType)
    }


    context(EveData)
    override fun ammo(shipType: ShipType, moduleType: ModuleType, chargeType: ChargeType): Boolean {
        return when {
            chargeType.isBomb() -> true
            chargeType.isDefenderMissile() -> true
            chargeType.isAutoTargetingMissile() -> chargeType.isEmpireNavyFactionItem
            chargeType.isTech2Item -> true
            moduleType.isEnergyWeapon() -> isElitePirateCrystalProperForShipType(shipType, chargeType)
            moduleType.isHybridWeapon() -> isElitePirateHybridChargeProperForShipType(shipType, chargeType)
            else -> chargeType.isElitePirateFactionItem
        }
    }


}

