//
// Shared utilities for AI score calculators - implementation
//

#include "AIScoreCalculatorSharedUtilities.hpp"

#include "src/main/cpp/net/eagle0/shardok/ai/AIScoreUtilities.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/map/CoordsSet.hpp"

namespace shardok::score_calculator_internal {

auto EffectiveDistanceCache::GetOrCompute(
        const Unit* unit,
        const Coords& target,
        const ActionPointDistances* notBravingApd,
        const ActionPointDistances* bravingApd,
        const HexMap* hexMap) const -> DIST_T {
    CacheKey key{unit->unit_id(), target};
    auto it = cache.find(key);
    if (it != cache.end()) { return it->second; }

    CoordsSet targetSet(hexMap);
    targetSet.Add(target);

    DIST_T result = EffectiveDistance(unit, notBravingApd, bravingApd, targetSet);
    cache[key] = result;
    return result;
}

auto FleeStrategyScoreForState(const GameStateW& gameState, const PlayerId playerId) -> ScoreValue {
    ScoreValue scoreValue = 0.0;

    const auto* gameStatePtr = gameState.Get();
    const auto* units = gameStatePtr->units();

    for (const auto* unit : *units) {
        if (unit->status() != net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT) continue;

        if (unit->player_id() == playerId &&
            unit->battalion().type() != net::eagle0::shardok::storage::fb::BattalionTypeId_UNDEAD) {
            scoreValue += FLEE_UNIT_SCORE;

            if (unit->has_attached_hero() &&
                unit->attached_hero().control_info().controlled_unit_id() != -1) {
                scoreValue += FLEE_CONTROLLING_UNIT_SCORE;
            }
        }
    }

    return scoreValue;
}

// Forward declaration for recursive helper
static auto RecursiveAttackerMultiplierForTargetDistance(
        const Unit* attackingUnit,
        std::vector<TargetAndAttackLocations>::const_iterator& priorityListNext,
        const std::vector<TargetAndAttackLocations>::const_iterator& priorityListEnd,
        const std::vector<const Unit*>& occupants,
        const HexMap* map,
        const BattalionTypeSPtr& battType,
        const ActionPointDistances* notBravingApd,
        const ActionPointDistances* bravingApd,
        bool isLateGame) -> double;

static auto RecursiveAttackerMultiplierForTargetDistance(
        const Unit* attackingUnit,
        std::vector<TargetAndAttackLocations>::const_iterator& priorityListNext,
        const std::vector<TargetAndAttackLocations>::const_iterator& priorityListEnd,
        const std::vector<const Unit*>& occupants,
        const HexMap* map,
        const BattalionTypeSPtr& battType,
        const ActionPointDistances* notBravingApd,
        const ActionPointDistances* bravingApd,
        const bool isLateGame) -> double {
    if (priorityListNext == priorityListEnd) return 1.0;

    const auto& [target, attackLocations] = *priorityListNext;
    const Coords& topPriorityTarget = target;

    // If the target is unoccupied or is occupied by this player, give the maximum multiplier, but
    // also add the bonus for the next up in the priority list
    if (const Unit* occupant = occupants
                [topPriorityTarget.row() * map->column_count() + topPriorityTarget.column()];
        !occupant || occupant->player_id() == attackingUnit->player_id()) {
        return kMaxProximityBuf + RecursiveAttackerMultiplierForTargetDistance(
                                          attackingUnit,
                                          ++priorityListNext,
                                          priorityListEnd,
                                          occupants,
                                          map,
                                          battType,
                                          notBravingApd,
                                          bravingApd,
                                          isLateGame);
    }

    // Use optimized EffectiveDistance with pre-computed ActionPointDistances
    // attackLocations is already the CoordsSet of attack locations for this target
    const DIST_T distance =
            EffectiveDistance(attackingUnit, notBravingApd, bravingApd, attackLocations);

    return kMaxProximityBuf / (1 + distance / kDistanceDebufRatio);
}

auto AttackerMultiplierForTargetDistance(
        const Unit* attackingUnit,
        const std::vector<TargetAndAttackLocations>& priorityList,
        const std::vector<const Unit*>& occupants,
        const HexMap* map,
        const BattalionTypeSPtr& battType,
        const ActionPointDistances* notBravingApd,
        const ActionPointDistances* bravingApd,
        const bool isLateGame) -> double {
    auto iter = std::begin(priorityList);
    return RecursiveAttackerMultiplierForTargetDistance(
            attackingUnit,
            iter,
            std::end(priorityList),
            occupants,
            map,
            battType,
            notBravingApd,
            bravingApd,
            isLateGame);
}

}  // namespace shardok::score_calculator_internal
