//
// Shared utilities for AI score calculators
// This file is private to the ai/score package
//

#ifndef EAGLE0_SHARDOK_AI_SCORE_PRIVATE_AI_SCORE_CALCULATOR_SHARED_UTILITIES_HPP
#define EAGLE0_SHARDOK_AI_SCORE_PRIVATE_AI_SCORE_CALCULATOR_SHARED_UTILITIES_HPP

#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wthread-safety-analysis"
#pragma GCC diagnostic ignored "-Wunused-result"
#include <gtl/phmap.hpp>
#pragma GCC diagnostic pop

#include "src/main/cpp/net/eagle0/shardok/ai/AIAttackGroups.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai/AIScoreUtilities.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/BattalionType.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/GameStateW.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/ShardokCTypes.h"
#include "src/main/cpp/net/eagle0/shardok/library/action_point_distances/ActionPointDistances.hpp"
#include "src/main/flatbuffer/net/eagle0/shardok/storage/game_state_generated.h"
#include "src/main/flatbuffer/net/eagle0/shardok/storage/hex_map_generated.h"
#include "src/main/flatbuffer/net/eagle0/shardok/storage/unit_generated.h"

namespace shardok::score_calculator_internal {

using HexMap = net::eagle0::shardok::storage::fb::HexMap;
using Unit = net::eagle0::shardok::storage::fb::Unit;

// Scoring constants shared across all calculators
constexpr double UNITS_BASE_MULTIPLIER = 0.05;
constexpr double FLEE_UNIT_SCORE = -10000;
constexpr double FLEE_CONTROLLING_UNIT_SCORE = -10000;
constexpr double CAPTURED_UNIT_SCORE = -10000;
constexpr double CAPTURED_VIP_SCORE = -25000;
constexpr double kMaxProximityBuf = 1.5;
constexpr double kDistanceDebufRatio = 8.0;

/// Structure to hold separated attacker/defender unit scores
/// Used by scorers that combine attacker and defender unit values separately.
struct UnitsScoreComponents {
    double attackerUnitsValue;
    double defenderUnitsValue;
};

/// Memoization cache for EffectiveDistance calls
struct EffectiveDistanceCache {
    struct CacheKey {
        UnitId unitId;
        Coords target;
        [[nodiscard]] bool operator==(const CacheKey& other) const {
            return unitId == other.unitId && target == other.target;
        }
    };

    struct CacheKeyHash {
        [[nodiscard]] size_t operator()(const CacheKey& key) const {
            return std::hash<UnitId>{}(key.unitId) ^ (std::hash<int>{}(key.target.row()) << 1) ^
                   (std::hash<int>{}(key.target.column()) << 2);
        }
    };

    mutable gtl::flat_hash_map<CacheKey, DIST_T, CacheKeyHash> cache;

    [[nodiscard]] DIST_T GetOrCompute(
            const Unit* unit,
            const Coords& target,
            const ActionPointDistances* notBravingApd,
            const ActionPointDistances* bravingApd,
            const HexMap* hexMap) const;
};

/// Calculate score for FLEE strategy
/// Returns negative score based on fleeing units
[[nodiscard]] auto FleeStrategyScoreForState(const GameStateW& gameState, PlayerId playerId)
        -> ScoreValue;

/// Calculate attacker multiplier based on distance to priority targets
/// This is used to weight attacker units by their proximity to objectives
[[nodiscard]] 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,
        bool isLateGame) -> double;

}  // namespace shardok::score_calculator_internal

#endif  // EAGLE0_SHARDOK_AI_SCORE_PRIVATE_AI_SCORE_CALCULATOR_SHARED_UTILITIES_HPP
