package com.dowstats.service.datamaps

import com.dowstats.Metadata.Requirements
import com.dowstats.data.dto.controllers.ResearchEffectDto
import com.dowstats.data.dto.controllers.WeaponResearchDto
import com.dowstats.data.dto.controllers.research.ResearchShortDto
import com.dowstats.data.entities.research.Research
import com.dowstats.data.entities.weapon.Weapon
import com.dowstats.data.repositories.ResearchRepository
import org.springframework.stereotype.Component

/**
 * Builds detailed information about how researches affect a weapon's fields.
 * Maps research modifier references to weapon fields.
 */
@Component
class WeaponResearchEffectMappingComponent(
    val researchRepository: ResearchRepository,
) {

    /**
     * Mapping from modifier reference name (without modifiers prefix and .lua suffix)
     * to the corresponding weapon field name.
     */
    private val referenceToField: Map<String, String> = mapOf(
        "accuracy_moving_reduction_weapon_modifier" to "accuracyReductionMoving",
        "accuracy_ranged_weapon_modifier" to "accuracy",
        "accuracy_weapon_modifier" to "accuracy",
        "armour_piercing_weapon_modifier" to "armourPiercing",
        "max_damage_weapon_modifier" to "maxDamage",
        "max_range_weapon_modifier" to "maxRange",
        "min_damage_weapon_modifier" to "minDamage",
        "reload_time_weapon_modifier" to "reloadTime",
        "setup_time_weapon_modifier" to "setupTime",
    )

    /**
     * Builds a list of WeaponResearchDto for a given weapon,
     * describing how each affecting research modifies the weapon's fields.
     */
    fun buildEffects(weapon: Weapon): List<WeaponResearchDto> {
        val weaponTarget = weapon.filename?.replace(".rgd", "") ?: return emptyList()

        return weapon.affectedResearches.mapNotNull { research ->
            val effects = research.researchModifiers
                .filter { modifier ->
                    modifier.target == weaponTarget && modifier.reference != null
                }
                .mapNotNull { modifier ->
                    val refName = extractRefName(modifier.reference!!)
                    val field = referenceToField[refName] ?: return@mapNotNull null

                    ResearchEffectDto(
                        field = field,
                        usageType = modifier.usageType,
                        modifierValue = modifier.value,
                    )
                }

            if (effects.isEmpty()) return@mapNotNull null

            WeaponResearchDto(
                research = research.toResearchShortDto(),
                researchRequired = getRequiredResearches(research),
                effects = effects,
            )
        }
    }

    /**
     * Returns researches required by the given research (its prerequisites).
     * Handles both "required_research" (excluding "must not be complete" ones,
     * since they are mutually exclusive rather than required)
     * and "required_research_either" requirements.
     */
    private fun getRequiredResearches(research: Research): List<ResearchShortDto> {
        val modId = research.modId ?: return emptyList()

        val required = research.researchRequirements
            .filter { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIREMENT_RESEARCH }
            .filter { it.value?.split(";")?.last() != "true" }
            .mapNotNull { req ->
                req.value?.split(";")?.first()
                    ?.replace(".lua", ".rgd")
                    ?.replace("research\\", "")
                    ?.let { if (it.endsWith(".rgd")) it else "$it.rgd" }
                    ?.let { researchRepository.findFirstByModIdAndFilename(modId, it)?.toResearchShortDto() }
            }

        val requiredEither = research.researchRequirements
            .filter { it.reference?.getPureReference() == Requirements.REFERENCE_REQUIRED_RESEARCH_EITHER }
            .flatMap { req ->
                req.value?.split(";").orEmpty().mapNotNull { r ->
                    val researchFileName = r.split("\\").last().replace(".lua", ".rgd").let { resFile ->
                        if (resFile.endsWith(".rgd")) resFile else "$resFile.rgd"
                    }
                    researchRepository.findFirstByModIdAndFilename(modId, researchFileName)?.toResearchShortDto()
                }
            }

        return (required + requiredEither).distinct()
    }

    private fun String.getPureReference(): String =
        this.split("\\").last()
            .replace(".lua", "")
            .replace(".rgd", "")

    /**
     * Extracts the short modifier name from a full reference path.
     * e.g. modifiers/max_damage_weapon_modifier.lua -> max_damage_weapon_modifier
     */
    private fun extractRefName(reference: String): String =
        reference.split("\\").first()
            .replace(".lua", "")
            .replace(".rgd", "")
}