package theorycrafter.tournaments

import eve.data.EveData
import eve.data.ShipType
import theorycrafter.tournaments.DraftCompositionRules.ShipPosition


/**
 * The interface for draft composition rules.
 *
 * Each composition has a fixed number of ship "positions", where each position accepts a set of ship types.
 */
interface DraftCompositionRules: TournamentRules.CompositionRules {


    /**
     * The list of positions, in their natural order.
     */
    val positions: List<ShipPosition>


    /**
     * All the legal ship types, in any position.
     */
    val allLegalShipTypes: Set<ShipType>


    /**
     * The interface for position objects.
     */
    data class ShipPosition(


        /**
         * The name of the position that will be displayed to the user.
         */
        val name: String,


        /**
         * A string that can be prefixed to the ship name in this position to indicate the position.
         */
        val prefix: String,


        /**
         * The amount of points associated with this position.
         */
        val points: Int,


        /**
         * The set of ships allowed in this position.
         */
        val legalShipTypes: Set<ShipType>,


    )


}


/**
 * Specifies a single [ShipPosition] by providing the ship names.
 */
class ShipPositionSpec(
    val name: String,
    val prefix: String,
    val points: Int,
    val shipTypeNames: List<String>,
)


/**
 * A base implementation of [DraftCompositionRules].
 */
abstract class BasicDraftCompositionRules(
    protected val eveData: EveData,
    positionSpecs: List<ShipPositionSpec>,
): DraftCompositionRules {


    override val positions: List<ShipPosition> = positionSpecs.map {
        ShipPosition(
            name = it.name,
            prefix = it.prefix,
            points = it.points,
            legalShipTypes = it.shipTypeNames.mapTo(mutableSetOf()) {
                name -> eveData.shipType(name)
            }
        )
    }


    private val positionByShipType: Map<ShipType, ShipPosition> = buildMap {
        for (position in positions) {
            for (shipType in position.legalShipTypes) {
                put(shipType, position)
            }
        }
    }


    override val allLegalShipTypes: Set<ShipType>
        get() = positionByShipType.keys


    override fun isShipLegal(shipType: ShipType) = shipType in allLegalShipTypes


    override fun shipsCost(shipTypes: List<ShipType?>): List<Int> {
        return shipTypes.map {
            if (it == null)
                0
            else
                positionByShipType[it]?.points ?: 0
        }
    }


    override fun compositionShipsIllegalityReason(composition: Composition): List<String?> {
        val shipPositions = buildList {
            addAll(positions)

            for (index in positions.size until composition.size) {
                val ship = composition[index]
                add(
                    if (ship == null)
                        null
                    else
                        positionByShipType[ship.shipType]
                )
            }
        }

        val activePositionCounts = shipPositions.withIndex()
            .filter {
                composition[it.index].let { ship ->
                    (ship != null) && ship.active
                }
            }
            .groupingBy { it.value }
            .eachCount()

        return shipPositions.withIndex().map { (index, position) ->
            val ship = composition[index]
            when {
                ship == null -> null  // Empty slot
                position == null -> "Illegal ship type"  // Totally illegal ship (for any position)
                ship.shipType !in position.legalShipTypes -> "Ship type not allowed in this position"
                ship.active && (activePositionCounts[position]!! >= 2) -> "At most one position may be active"
                else -> null
            }
        }
    }


    override fun compositionIconShipTypes(composition: Composition): List<ShipType?> {
        // Return the first non logi ship and the logi ship

        val compShipTypes = composition.ships.mapNotNull { it?.shipType }
        val mainNonLogiShipType = compShipTypes.firstOrNull {
            with(eveData) { !it.isLogistics }
        }
        val logi = compShipTypes.firstOrNull {
            with(eveData) { it.isLogistics }
        }

        return listOf(mainNonLogiShipType, logi)
    }


}

