package com.dowstats.service.postparsing

import com.dowstats.data.entities.*
import com.dowstats.data.entities.ability.Ability
import com.dowstats.data.entities.addon.BuildingAddon
import com.dowstats.data.entities.building.Building
import com.dowstats.data.entities.research.Research
import com.dowstats.data.entities.sergant.Sergeant
import com.dowstats.data.entities.unit.DowUnit
import com.dowstats.data.entities.weapon.Weapon
import com.dowstats.data.repositories.*
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service


@Service
class PostParsingService @Autowired constructor(
    val researchRepository: ResearchRepository,
    val addonRepository: AddonRepository,
    val unitRepository: UnitRepository,
    val abilityRepository: AbilityRepository,
    val sergeantRepository: SergeantRepository,
    val buildingRepository: BuildingRepository,
    val weaponRepository: WeaponRepository,
) {

    val log = LoggerFactory.getLogger(PostParsingService::class.java)

    fun bindResearch(mod: Mod) {
        try {
            val modId = mod.id!!
            val researches = researchRepository.findAllByModId(modId)
            val addons = addonRepository.findAllByModId(modId)
            val abilities = abilityRepository.findAllByModId(modId)
            val units = unitRepository.findByModIdAndRace(modId, null)
            val sergeants = sergeantRepository.findAllByModId(modId)
            val buildings = buildingRepository.findByModIdAndRace(modId, null)
            val weapons = weaponRepository.findAllByModId(modId)

            bindResearchUnits(units, researches)
            bindResearchSergeants(sergeants, researches)
            bindResearchBuildings(buildings, researches)
            bindResearchWeapons(weapons, researches)

            bindAddonUnits(units, addons)
            bindAddonSergeants(sergeants, addons)
            bindAddonBuildings(buildings, addons)
            bindAddonWeapons(weapons, addons)

            bindBuildingUnits(units, buildings)
            bindBuildingSergeants(sergeants, buildings)
            bindBuildingToAffectedBuildings(buildings)
            bindBuildingWeapons(weapons, buildings)

            bindAbilityUnits(units, abilities)
            bindAbilitySergeants(sergeants, abilities)
            bindAbilityBuildings(buildings, abilities)
            bindAbilityWeapons(weapons, abilities)

            bindSergeantUnits(units, sergeants)
            bindSergeantSergeants(sergeants)
            bindSergeantBuildings(buildings, sergeants)
            bindSergeantWeapons(weapons, sergeants)

            bindUnitUnits(units)
            bindUnitSergeants(sergeants, units)
            bindUnitBuildings(buildings, units)
            bindUnitWeapons(weapons, units)

        } catch (e: Exception) {
            log.warn("Error occurred during bind researches", e)
        }

    }

    private fun bindResearchUnits(units: List<DowUnit>, researches: List<Research>) {

        val unitsWithBindings: List<DowUnit> = researches.flatMap { research ->
            research.researchModifiers.mapNotNull { modifier ->
                units.find {
                    it.filenameSquad?.replace(".rgd", "") == modifier.target ||
                            it.filenameUnit?.replace(".rgd", "") == modifier.target
                }
                    ?.let {
                        it.affectedResearches.add(research)
                        it
                    }
            }
        }
        unitRepository.saveAll(unitsWithBindings)
        log.info("Successfully bind researches to units")
    }

    private fun bindResearchSergeants(sergeants: List<Sergeant>, researches: List<Research>) {

        val sergeantsWithBindings: List<Sergeant> = researches.flatMap { research ->
            research.researchModifiers.mapNotNull { modifier ->
                sergeants.find { it.filename.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedResearches.add(research)
                    it
                }
            }
        }
        sergeantRepository.saveAll(sergeantsWithBindings)
        log.info("Successfully bind researches to sergeants")
    }

    private fun bindResearchBuildings(buildings: List<Building>, researches: List<Research>) {

        val buildingsWithBindings: List<Building> = researches.flatMap { research ->
            research.researchModifiers.mapNotNull { modifier ->
                buildings.find { it.filename?.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedResearches.add(research)
                    it
                }
            }
        }
        buildingRepository.saveAll(buildingsWithBindings)
        log.info("Successfully bind researches to buildings")
    }

    private fun bindResearchWeapons(weapons: List<Weapon>, researches: List<Research>) {

        val weaponsWithBindings: List<Weapon> = researches.flatMap { research ->
            research.researchModifiers.mapNotNull { modifier ->
                weapons.find { it.filename?.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedResearches.add(research)
                    it
                }
            }
        }
        weaponRepository.saveAll(weaponsWithBindings)
        log.info("Successfully bind researches to weapons")
    }

    private fun bindAddonUnits(units: List<DowUnit>, addons: List<BuildingAddon>) {

        val unitsWithBindings: List<DowUnit> = addons.flatMap { addon ->
            addon.addonModifiers.mapNotNull { modifier ->
                units.find {
                    it.filenameSquad?.replace(".rgd", "") == modifier.target ||
                            it.filenameUnit?.replace(".rgd", "") == modifier.target
                }
                    ?.let {
                        it.affectedAddons.add(addon)
                        it
                    }
            }
        }
        unitRepository.saveAll(unitsWithBindings)
        log.info("Successfully bind addons to units")
    }

    private fun bindAddonSergeants(sergeants: List<Sergeant>, addons: List<BuildingAddon>) {

        val sergeantsWithBindings: List<Sergeant> = addons.flatMap { addon ->
            addon.addonModifiers.mapNotNull { modifier ->
                sergeants.find { it.filename.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedAddons.add(addon)
                    it
                }
            }
        }
        sergeantRepository.saveAll(sergeantsWithBindings)
        log.info("Successfully bind addons to sergeants")
    }

    private fun bindAddonBuildings(buildings: List<Building>, addons: List<BuildingAddon>) {

        val buildingsWithBindings: List<Building> = addons.flatMap { addon ->
            addon.addonModifiers.mapNotNull { modifier ->
                buildings.find { it.filename?.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedAddons.add(addon)
                    it
                }
            }
        }
        buildingRepository.saveAll(buildingsWithBindings)
        log.info("Successfully bind addons to buildings")
    }

    private fun bindAddonWeapons(weapons: List<Weapon>, addons: List<BuildingAddon>) {

        val weaponsWithBindings: List<Weapon> = addons.flatMap { addon ->
            addon.addonModifiers.mapNotNull { modifier ->
                weapons.find { it.filename?.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedAddons.add(addon)
                    it
                }
            }
        }
        weaponRepository.saveAll(weaponsWithBindings)
        log.info("Successfully bind addons to weapons")
    }

    private fun bindBuildingUnits(units: List<DowUnit>, buildings: List<Building>) {

        val unitsWithBindings: List<DowUnit> = buildings.flatMap { building ->
            building.modifiers.mapNotNull { modifier ->
                units.find {
                    it.filenameSquad?.replace(".rgd", "") == modifier.target ||
                            it.filenameUnit?.replace(".rgd", "") == modifier.target
                }
                    ?.let {
                        it.affectedBuildings.add(building)
                        it
                    }
            }
        }
        unitRepository.saveAll(unitsWithBindings)
        log.info("Successfully bind buildings to units")
    }

    private fun bindBuildingSergeants(sergeants: List<Sergeant>, buildings: List<Building>) {

        val sergeantsWithBindings: List<Sergeant> = buildings.flatMap { building ->
            building.modifiers.mapNotNull { modifier ->
                sergeants.find { it.filename.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedBuildings.add(building)
                    it
                }
            }
        }
        sergeantRepository.saveAll(sergeantsWithBindings)
        log.info("Successfully bind buildings to sergeants")
    }

    private fun bindBuildingToAffectedBuildings(buildings: List<Building>,) {

        val buildingsWithBindings: List<Building> = buildings.flatMap { building ->
            building.modifiers.mapNotNull { modifier ->
                buildings.find { it.filename?.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedBuildings.add(building)
                    it
                }
            }
        }
        buildingRepository.saveAll(buildingsWithBindings)
        log.info("Successfully bind buildings to each other")
    }

    private fun bindBuildingWeapons(weapons: List<Weapon>, buildings: List<Building>) {

        val weaponsWithBindings: List<Weapon> = buildings.flatMap { building ->
            building.modifiers.mapNotNull { modifier ->
                weapons.find { it.filename?.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedBuildings.add(building)
                    it
                }
            }
        }
        weaponRepository.saveAll(weaponsWithBindings)
        log.info("Successfully bind buildings to weapons")
    }

    // ------------------------------------------------------------

    private fun bindAbilityUnits(units: List<DowUnit>, abilities: List<Ability>) {
        val unitsWithBindings: List<DowUnit> = abilities.flatMap { ability ->
            ability.abilityModifiers.mapNotNull { modifier ->
                units.find {
                    it.filenameSquad?.replace(".rgd", "") == modifier.target ||
                            it.filenameUnit?.replace(".rgd", "") == modifier.target
                }
                    ?.let {
                        it.affectedAbilities.add(ability)
                        it
                    }
            }
        }
        unitRepository.saveAll(unitsWithBindings)
        log.info("Successfully bind abilities to units")
    }

    private fun bindAbilitySergeants(sergeants: List<Sergeant>, abilities: List<Ability>) {
        val sergeantsWithBindings: List<Sergeant> = abilities.flatMap { ability ->
            ability.abilityModifiers.mapNotNull { modifier ->
                sergeants.find { it.filename.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedAbilities.add(ability)
                    it
                }
            }
        }
        sergeantRepository.saveAll(sergeantsWithBindings)
        log.info("Successfully bind abilities to sergeants")
    }

    private fun bindAbilityBuildings(buildings: List<Building>, abilities: List<Ability>) {
        val buildingsWithBindings: List<Building> = abilities.flatMap { ability ->
            ability.abilityModifiers.mapNotNull { modifier ->
                buildings.find { it.filename?.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedAbilities.add(ability)
                    it
                }
            }
        }
        buildingRepository.saveAll(buildingsWithBindings)
        log.info("Successfully bind abilities to buildings")
    }

    private fun bindAbilityWeapons(weapons: List<Weapon>, abilities: List<Ability>) {
        val weaponsWithBindings: List<Weapon> = abilities.flatMap { ability ->
            ability.abilityModifiers.mapNotNull { modifier ->
                weapons.find { it.filename?.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedAbilities.add(ability)
                    it
                }
            }
        }
        weaponRepository.saveAll(weaponsWithBindings)
        log.info("Successfully bind abilities to weapons")
    }

    // ------------------------------------------------------------

    private fun bindSergeantUnits(units: List<DowUnit>, sergeants: List<Sergeant>) {
        val unitsWithBindings: List<DowUnit> = sergeants.flatMap { sergeant ->
            sergeant.sergeantModifiers.mapNotNull { modifier ->
                units.find {
                    it.filenameSquad?.replace(".rgd", "") == modifier.target ||
                            it.filenameUnit?.replace(".rgd", "") == modifier.target
                }
                    ?.let {
                        it.affectedSergeants.add(sergeant)
                        it
                    }
            }
        }
        unitRepository.saveAll(unitsWithBindings)
        log.info("Successfully bind sergeants to units")
    }

    private fun bindSergeantSergeants(sergeants: List<Sergeant>) {
        val sergeantsWithBindings: List<Sergeant> = sergeants.flatMap { sergeant ->
            sergeant.sergeantModifiers.mapNotNull { modifier ->
                sergeants.find { it.filename.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedSergeants.add(sergeant)
                    it
                }
            }
        }
        sergeantRepository.saveAll(sergeantsWithBindings)
        log.info("Successfully bind sergeants to sergeants")
    }

    private fun bindSergeantBuildings(buildings: List<Building>, sergeants: List<Sergeant>) {
        val buildingsWithBindings: List<Building> = sergeants.flatMap { sergeant ->
            sergeant.sergeantModifiers.mapNotNull { modifier ->
                buildings.find { it.filename?.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedSergeants.add(sergeant)
                    it
                }
            }
        }
        buildingRepository.saveAll(buildingsWithBindings)
        log.info("Successfully bind sergeants to buildings")
    }

    private fun bindSergeantWeapons(weapons: List<Weapon>, sergeants: List<Sergeant>) {
        val weaponsWithBindings: List<Weapon> = sergeants.flatMap { sergeant ->
            sergeant.sergeantModifiers.mapNotNull { modifier ->
                weapons.find { it.filename?.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedSergeants.add(sergeant)
                    it
                }
            }
        }
        weaponRepository.saveAll(weaponsWithBindings)
        log.info("Successfully bind sergeants to weapons")
    }



    // ------------------------------------------------------------

    private fun bindUnitUnits(units: List<DowUnit>) {
        val unitsWithBindings: List<DowUnit> = units.flatMap { unit ->
            unit.unitModifiers.mapNotNull { modifier ->
                units.find {
                    it.filenameSquad?.replace(".rgd", "") == modifier.target ||
                            it.filenameUnit?.replace(".rgd", "") == modifier.target
                }
                    ?.let {
                        it.affectedUnits.add(unit)
                        it
                    }
            }
        }
        unitRepository.saveAll(unitsWithBindings)
        log.info("Successfully bind units to units")
    }

    private fun bindUnitSergeants(sergeants: List<Sergeant>, units: List<DowUnit>) {
        val sergeantsWithBindings: List<Sergeant> = units.flatMap { unit ->
            unit.unitModifiers.mapNotNull { modifier ->
                sergeants.find { it.filename.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedUnits.add(unit)
                    it
                }
            }
        }
        sergeantRepository.saveAll(sergeantsWithBindings)
        log.info("Successfully bind units to sergeants")
    }

    private fun bindUnitBuildings(buildings: List<Building>, units: List<DowUnit>) {
        val buildingsWithBindings: List<Building> = units.flatMap { unit ->
            unit.unitModifiers.mapNotNull { modifier ->
                buildings.find { it.filename?.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedUnits.add(unit)
                    it
                }
            }
        }
        buildingRepository.saveAll(buildingsWithBindings)
        log.info("Successfully bind units to buildings")
    }

    private fun bindUnitWeapons(weapons: List<Weapon>, units: List<DowUnit>) {
        val weaponsWithBindings: List<Weapon> = units.flatMap { unit ->
            unit.unitModifiers.mapNotNull { modifier ->
                weapons.find { it.filename?.replace(".rgd", "") == modifier.target }?.let {
                    it.affectedUnits.add(unit)
                    it
                }
            }
        }
        weaponRepository.saveAll(weaponsWithBindings)
        log.info("Successfully bind units to weapons")
    }

}
