package com.dowstats.service.datamaps

import com.dowstats.data.dto.controllers.JumpDto
import com.dowstats.data.dto.controllers.RaceUnits
import com.dowstats.data.dto.controllers.SergeantDto
import com.dowstats.data.dto.controllers.UnitFullDto
import com.dowstats.data.entities.unit.DowUnit
import com.dowstats.data.entities.unit.DowUnitObject.toUnitDto
import com.dowstats.data.entities.Race
import com.dowstats.data.entities.sergant.Sergeant
import com.dowstats.data.repositories.RaceRepository
import com.dowstats.data.repositories.UnitRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

@Service
class DowUnitMappingService @Autowired constructor(
    val unitRepository: UnitRepository,
    val raceRepository: RaceRepository,
    val requirementsMappingComponent: RequirementsMappingComponent,
) {

    fun findUnitsByMod(modId: Long): List<RaceUnits> {
        val races = raceRepository.findRacesByMod(modId)
        return getAllUnits(modId).groupBy { it.race }.map {raceUnits ->
            toRaceUnitsDto(raceUnits.value, races.first { it.id == raceUnits.key?.id } )
        }
    }

    fun getUnitFullDto(unitId: Long): UnitFullDto {
        val unit = unitRepository.findById(unitId).get()
        val requirementDto = requirementsMappingComponent
            .getRequirements(unit.unitRequirements.toList(),
                unit.modId!!,
            )

        val weapons = unit.weapons?.map { unitWeapon ->
            unitWeapon.toWeaponSlotDto()
        }?.toSet() ?: emptySet()

        val jumpsDto = unit.jump?.let { jump ->
            JumpDto(
                jump.chargeRegeneration,
                jump.goUpTime,
                jump.jumpDistanceMax,
                jump.breakdownTime,
                jump.chargeJumpCostMin,
                jump.jumpHeight,
                jump.chargeMax,
                jump.setupTime,
                jump.chargeJumpCostMax,
                jump.goDownTime,
                jump.teleport,
                jump.jumpTimeMax,
                jump.jumpTimeMin,
                jump.jumpMinHeight,
                requirementsMappingComponent.getRequirements(jump.jumpRequirements.toList(), unit.modId!!)
            )
        }

       return unitRepository.findById(unitId).get().toDto(
           weapons,
           unit.sergeants?.map { getSergeantFullDto(it) }?.toSet() ?: emptySet(),
           requirementDto,
           jumpsDto
       )
    }

    fun findUnitsByModAndRace(modId: Long, race: String): RaceUnits {
        val units = getAllUnits(modId, race)
        val raceEntity = race.let{ raceRepository.findById(race) ?: throw Exception("Race $race not found") }
        return toRaceUnitsDto(units, raceEntity)
    }

    private fun getSergeantFullDto(sergeant: Sergeant): SergeantDto {
        val requirementDto = requirementsMappingComponent
            .getRequirements(sergeant.sergeantRequirements.toList(),
                sergeant.modId!!,
            )

        val weapons = sergeant.weapons?.map { sergeantWeapon ->
            sergeantWeapon.toWeaponSlotDto()
        }?.toSet() ?: emptySet()

        return sergeant.toDto(weapons, requirementDto)
    }

    private fun toRaceUnitsDto(units: List<DowUnit>, race: Race): RaceUnits {
        val infantry = units
            .filter { it.capInfantry?.let { it > 0 } ?: false || it.reinforceCostPopulation?.let { it > 0 } ?: false }
            .toUnitDto()

        val tech = units
            .filter { it.capSupport?.let { it > 0 } ?: false }
            .toUnitDto()

        val support = units.filter {
            it.capInfantry?.let { it == 0 } ?: false && it.reinforceCostPopulation?.let { it == 0.0 } ?: true &&
                    it.capSupport?.let { it == 0 } ?: false
        }
            .toUnitDto()

        return RaceUnits(race.toDto(), infantry, tech, support)
    }


    private fun getAllUnits(modId: Long, race: String? = null): List<DowUnit> {
        val raceEntity = race?.let{ raceRepository.findById(race) ?: throw Exception("Race $race not found") }
        return unitRepository.findByModIdAndRace(modId, raceEntity)
    }

}