package com.dowstats.data.entities.unit

import com.dowstats.data.dto.controllers.*
import com.dowstats.data.dto.controllers.EntityCompressDtoObject.compressDto
import com.dowstats.data.dto.controllers.SergeantUnitShortDtoObject.toShortDtoSet
import com.dowstats.data.dto.controllers.WeaponUnitShortDtoObject.toShortDtoSet
import com.dowstats.data.entities.ArmorType
import com.dowstats.data.entities.Race
import com.dowstats.data.entities.ability.Ability
import com.dowstats.data.entities.sergant.Sergeant
import com.dowstats.data.entities.addon.BuildingAddon
import com.dowstats.data.entities.building.Building
import com.dowstats.data.entities.deathexplosion.DeathExplosion
import com.dowstats.data.entities.research.Research
import com.dowstats.data.entities.research.ResearchModifiers
import com.dowstats.data.entities.weapon.Weapon
import com.dowstats.data.entities.unit.Jump
import jakarta.persistence.*


@Entity
@Table(name = "units")
class DowUnit {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long? = null

    @ManyToOne
    @JoinColumn(name = "race_id", nullable = false)
    var race: Race? = null

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "armour_type_id", nullable = false)
    var armorType: ArmorType? = null

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "armour_type_2_id")
    var armorType2: ArmorType? = null

    @ManyToMany(mappedBy = "units")
    var buildFrom: Set<Building> = setOf()

    var name: String? = null
    var description: String? = null
    var filenameSquad: String? = null
    var filenameUnit: String? = null
    var buildCostRequisition: Double? = null
    var buildCostPower: Double? = null
    var buildCostPopulation: Double? = null
    var buildCostFaith: Double? = null
    var buildCostSouls: Double? = null
    var buildCostTime: Double? = null
    var capInfantry: Int? = null
    var capSupport: Int? = null
    var squadStartSize: Int? = null
    var squadMaxSize: Int? = null
    var squadLimit: Int? = null
    var health: Int? = null
    var armour: Double? = null
    var healthRegeneration: Double? = null
    var moraleDeathPenalty: Int? = null
    var repairMax: Int? = null
    var repairSpeed: Int? = null
    var repairCostPercent: Int? = null
    var moraleMax: Int? = null
    var moraleBroken: Int? = null
    var moraleRegeneration: Int? = null
    var mass: Int? = null
    var upTime: Double? = null
    var moveSpeed: Int? = null
    var sightRadius: Int? = null
    var detectRadius: Int? = null
    var reinforceCostRequisition: Double? = null
    var reinforceCostPower: Double? = null
    var reinforceCostPopulation: Double? = null
    var reinforceCostFaith: Double? = null
    var reinforceCostSouls: Double? = null
    var reinforceTime: Double? = null
    var maxSergeants: Int? = null
    var faithIncome: Double? = null
    var powerIncome: Double? = null
    var requisitionIncome: Double? = null
    var mobValue: Double? = null
    var haveReinforceMenu: Boolean = false
    var uiIndexHint: Int = 0
    var uiHotkeyName: String? = null
    var icon: String? = null
    var modId: Long? = null

    @OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL])
    var sergeants: MutableSet<Sergeant>? = null

    @OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL])
    var weapons: MutableSet<UnitWeapon>? = null

    @OneToMany(cascade = [CascadeType.ALL])
    @JoinTable(name = "unit_death_explosion",
        joinColumns = [JoinColumn(name = "unit_id")],
        inverseJoinColumns = [JoinColumn(name = "death_explosion_id")])
    var deathExplosions: MutableSet<DeathExplosion> = mutableSetOf()

    @ManyToMany
    @JoinTable(name = "unit_abilities",
        joinColumns = [JoinColumn(name = "unit_id")],
        inverseJoinColumns = [JoinColumn(name = "ability_id")])
    var abilities: MutableSet<Ability> = mutableSetOf()


    @ManyToMany
    @JoinTable(name = "researches_affected_units",
        joinColumns = [JoinColumn(name = "unit_id")],
        inverseJoinColumns = [JoinColumn(name = "research_id")])
    var affectedResearches: MutableSet<Research> = mutableSetOf()

    @ManyToMany
    @JoinTable(name = "addon_affected_units",
        joinColumns = [JoinColumn(name = "unit_id")],
        inverseJoinColumns = [JoinColumn(name = "addon_id")])
    var affectedAddons: MutableSet<BuildingAddon> = mutableSetOf()

    @ManyToMany
    @JoinTable(name = "building_affected_units",
        joinColumns = [JoinColumn(name = "unit_id")],
        inverseJoinColumns = [JoinColumn(name = "building_id")])
    var affectedBuildings: MutableSet<Building> = mutableSetOf()


    @ManyToMany
    @JoinTable(name = "unit_affected_units",
        joinColumns = [JoinColumn(name = "unit_id_affected")],
        inverseJoinColumns = [JoinColumn(name = "unit_id")])
    var affectedUnits: MutableSet<DowUnit> = mutableSetOf()

    @ManyToMany
    @JoinTable(name = "sergeant_affected_units",
        joinColumns = [JoinColumn(name = "unit_id")],
        inverseJoinColumns = [JoinColumn(name = "sergeant_id")])
    var affectedSergeants: MutableSet<Sergeant> = mutableSetOf()

    @ManyToMany
    @JoinTable(name = "ability_affected_units",
        joinColumns = [JoinColumn(name = "unit_id")],
        inverseJoinColumns = [JoinColumn(name = "ability_id")])
    var affectedAbilities: MutableSet<Ability> = mutableSetOf()

    @ManyToMany
    @JoinTable(name = "unit_affected_units",
        joinColumns = [JoinColumn(name = "unit_id")],
        inverseJoinColumns = [JoinColumn(name = "unit_id_affected")])
    var affectedOnUnits: MutableSet<DowUnit> = mutableSetOf()

    @ManyToMany
    @JoinTable(name = "units_affected_sergeants",
        joinColumns = [JoinColumn(name = "unit_id")],
        inverseJoinColumns = [JoinColumn(name = "sergeant_id")])
    var affectedOnSergeants: MutableSet<Sergeant> = mutableSetOf()

    @ManyToMany
    @JoinTable(name = "unit_affected_buildings",
        joinColumns = [JoinColumn(name = "unit_id")],
        inverseJoinColumns = [JoinColumn(name = "building_id")])
    var affectedOnBuildings: MutableSet<Building> = mutableSetOf()

    @ManyToMany
    @JoinTable( name = "unit_affected_weapons",
        joinColumns = [JoinColumn(name = "unit_id")],
        inverseJoinColumns = [JoinColumn(name = "weapon_id")])
    var affectedOnWeapons: MutableSet<Weapon> = mutableSetOf()

    @OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL])
    var unitModifiers: MutableSet<UnitModifiers> = mutableSetOf()

    @OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL])
    var unitMoraleBrakeModifiers: MutableSet<UnitMoraleBrakeModifiers> = mutableSetOf()

    @OneToMany(mappedBy = "unit", cascade = [CascadeType.ALL])
    var unitRequirements: MutableSet<UnitRequirements> = mutableSetOf()

    @OneToOne(mappedBy = "unit", cascade = [CascadeType.ALL])
    var jump: Jump? = null

    fun toDto(weapons: Set<WeaponSlotDto>, sergeantsDtos: Set<SergeantDto>, requirementDto: RequirementDto?, jumpsDto: JumpDto? = null): UnitFullDto =
        UnitFullDto(
            id!!,
            race?.toDto(),
            armorType?.toDto(),
            armorType2?.toDto(),
            name,
            description,
            filenameSquad!!,
            buildCostRequisition,
            buildCostPower,
            buildCostPopulation,
            buildCostFaith,
            buildCostSouls,
            buildCostTime,
            faithIncome,
            powerIncome,
            requisitionIncome,
            mobValue,
            capInfantry,
            capSupport,
            squadStartSize,
            squadMaxSize,
            squadLimit,
            health,
            armour,
            healthRegeneration,
            moraleDeathPenalty,
            moraleMax,
            moraleBroken,
            moraleRegeneration,
            mass,
            upTime,
            moveSpeed,
            sightRadius,
            detectRadius,
            reinforceCostRequisition,
            reinforceCostPower,
            reinforceCostPopulation,
            reinforceCostFaith,
            reinforceCostSouls,
            reinforceTime,
            repairMax,
            repairSpeed,
            repairCostPercent,
            maxSergeants,
            icon,
            modId!!,
            deathExplosions.map { it.toDto(modId!!) }.toSet(),
            sergeantsDtos,
            weapons,
            abilities.sortedWith ( compareBy<Ability> { it.activation?.id }.thenBy{ it.uiIndexHint.let { 0 - it } }.thenBy{ it.name}.thenBy{ it.filename}).map { it.toShortDto() }.toSet(),
            haveReinforceMenu,
            affectedResearches.map { it.toResearchShortDto() }.toSet(),
            affectedAddons.map { it.toShortDto() }.toSet(),
            unitModifiers.map { it.toDto() }.sortedBy { it.target },
            unitMoraleBrakeModifiers.map { it.toDto() }.sortedBy { it.target },
            AffectedDataDto(
                affectedOnUnits.map { it.compressDto() }.toSet(),
                affectedOnSergeants.toShortDtoSet(),
                affectedOnBuildings.map { it.compressDto() }.toSet(),
                affectedOnWeapons.toShortDtoSet(),
            ),
            requirementDto,
            uiHotkeyName,
            jumpsDto,
        )
}

object DowUnitObject {
    fun List<DowUnit>.toUnitDto(): List<UnitShortDto> =
        this.mapNotNull {
            val name = it.name ?: it.filenameSquad
            val icon = it.icon
            if (name == null || icon == null) null else UnitShortDto(name, icon, it.id!!,
                it.armorType?.name!!,
                (it.detectRadius ?: 0) > 0)
        }

}