//
// Abstract base class for AI score calculator implementations
// This file is private to the ai/score package
//

#ifndef EAGLE0_SHARDOK_AI_SCORE_PRIVATE_ABSTRACT_AI_SCORE_CALCULATOR_HPP
#define EAGLE0_SHARDOK_AI_SCORE_PRIVATE_ABSTRACT_AI_SCORE_CALCULATOR_HPP

#include <memory>
#include <unordered_map>

#include "AIScoreCalculatorSharedUtilities.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai/AIAttackGroups.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai/AIAttackLocations.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai/AIStrategy.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai/score/AIScoreCalculator.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/ActionPointDistancesCache.hpp"
#include "src/main/flatbuffer/net/eagle0/shardok/storage/game_state_generated.h"

namespace shardok {

using net::eagle0::shardok::storage::fb::BattalionTypeId;

using APDCache = std::shared_ptr<ActionPointDistancesCache>;
using ALCache = std::unique_ptr<AttackLocationsCache>;
using score_calculator_internal::UnitsScoreComponents;

/// Enum representing terminal game outcomes for score interpretation
enum class GameOutcome {
    ATTACKER_VICTORY,
    DEFENDER_VICTORY,
    DRAW,
    FLEE_OUTCOME  // Special outcome for FLEE strategy
};

/// Abstract base class providing shared functionality for AI score calculators.
/// Contains common member variables, accessor methods, and shared helper logic.
/// This class is private to the ai/score package.
class AbstractAIScoreCalculator : public AIScoreCalculator {
protected:
    AbstractAIScoreCalculator(
            int maxRounds,
            ActionPoints braveWaterCost,
            ActionPoints archeryActionPointCost,
            int meteorRange,
            double meteorCastVigorCost,
            int minimumFleeOddsThreshold,
            int desperateFleeThreshold,
            std::vector<BattalionTypeSPtr> battalionTypes,
            const APDCache &apdCache,
            const ALCache &alCache)
        : maxRounds_(maxRounds),
          braveWaterCost_(braveWaterCost),
          archeryActionPointCost_(archeryActionPointCost),
          meteorRange_(meteorRange),
          meteorCastVigorCost_(meteorCastVigorCost),
          minimumFleeOddsThreshold_(minimumFleeOddsThreshold),
          desperateFleeThreshold_(desperateFleeThreshold),
          battalionTypes_(std::move(battalionTypes)),
          apdCache_(apdCache),
          alCache_(alCache) {}

    // Protected accessor methods available to subclasses
    [[nodiscard]] inline auto GetBattalionType(BattalionTypeId typeId) const
            -> const BattalionTypeSPtr & {
        return battalionTypes_[typeId];
    }

    [[nodiscard]] auto GetApdCache() const -> const APDCache & { return apdCache_; }
    [[nodiscard]] auto GetAlCache() const -> const ALCache & { return alCache_; }
    [[nodiscard]] auto GetBraveWaterCost() const -> ActionPoints { return braveWaterCost_; }
    [[nodiscard]] auto GetArcheryActionPointCost() const -> ActionPoints {
        return archeryActionPointCost_;
    }
    [[nodiscard]] auto GetMaxRounds() const -> int { return maxRounds_; }
    [[nodiscard]] auto GetMeteorRange() const -> int { return meteorRange_; }
    [[nodiscard]] auto GetMeteorCastVigorCost() const -> double { return meteorCastVigorCost_; }
    [[nodiscard]] auto GetMinimumFleeOddsThreshold() const -> int {
        return minimumFleeOddsThreshold_;
    }
    [[nodiscard]] auto GetDesperateFleeThreshold() const -> int { return desperateFleeThreshold_; }

    // Protected helper method that both subclasses can use
    // Returns separate attacker and defender unit values
    [[nodiscard]] auto CalculateUnitsScoreComponents(
            const GameStateW &gameState,
            int roundsRemaining,
            bool attackerWantsCastles,
            bool defenderShouldScatter,
            const vector<TargetPriorityList> &attackerTargetPriorities,
            const MapId &mapId) const -> UnitsScoreComponents;

    // Helper to find the defender PlayerInfo
    [[nodiscard]] auto FindDefenderPlayerInfo(const GameStateW &gameState) const
            -> const net::eagle0::shardok::storage::fb::PlayerInfo *;

    // Calculate victory condition score for attacker strategies
    // Returns the raw victory condition score (not yet combined with units score)
    [[nodiscard]] auto CalculateAttackerVictoryConditionScore(
            const GameStateW &gameState,
            const AIStrategy &attackerStrategy,
            const CoordsSet &castleCoords) const -> ScoreValue;

    // Pure virtual methods for subclasses to interpret game outcomes
    [[nodiscard]] virtual auto InterpretDefenderOutcome(GameOutcome outcome) const
            -> ScoreValue = 0;
    [[nodiscard]] virtual auto InterpretAttackerOutcome(GameOutcome outcome) const
            -> ScoreValue = 0;

    // Shared implementation of DefenderScoreForState that uses InterpretDefenderOutcome
    [[nodiscard]] auto DefenderScoreForState(
            const GameStateW &gameState,
            const AIStrategy &defenderStrategy,
            const CoordsSet &castleCoords,
            int roundsRemaining) const -> ScoreValue;

    // Shared implementations that delegate to pure virtual methods
    [[nodiscard]] auto DefenderScatterStrategyScoreForState(
            const GameStateW &gameState,
            int roundsRemaining) const -> ScoreValue;

    [[nodiscard]] auto DefenderHoldCastlesStrategyScoreForState(
            const GameStateW &gameState,
            const CoordsSet &castleCoords,
            int roundsRemaining) const -> ScoreValue;

    // Pure virtual methods for combining defender scores
    [[nodiscard]] virtual auto CombineDefenderScatterScores(
            const UnitsScoreComponents &components) const -> ScoreValue = 0;

    [[nodiscard]] virtual auto CombineDefenderHoldCastlesScores(
            const UnitsScoreComponents &components,
            double victoryConditionScore,
            int roundsRemaining) const -> ScoreValue = 0;

    [[nodiscard]] virtual auto DefenderFleeStrategyScoreForState(const GameStateW &gameState) const
            -> ScoreValue = 0;

    // Shared implementation of AttackerScoreForState that uses InterpretAttackerOutcome
    [[nodiscard]] auto AttackerScoreForState(
            const GameStateW &gameState,
            const AIStrategy &attackerStrategy,
            const CoordsSet &castleCoords,
            int roundsRemaining) const -> ScoreValue;

    // Pure virtual method for attacker flee strategy scoring
    [[nodiscard]] virtual auto AttackerFleeStrategyScoreForState(const GameStateW &gameState) const
            -> ScoreValue = 0;

    // Pure virtual method for combining units score and victory condition score
    // This is where concrete scorers combine unit value and victory condition pressure.
    // Standard uses the raw unit-value difference; MCTS-optimized uses bounded scaling.
    [[nodiscard]] virtual auto CombineAttackerScores(
            const UnitsScoreComponents &components,
            double victoryConditionScore,
            int roundsRemaining) const -> ScoreValue = 0;

private:
    // Scalar settings extracted from SettingsGetter
    int maxRounds_;
    ActionPoints braveWaterCost_;
    ActionPoints archeryActionPointCost_;
    int meteorRange_;
    double meteorCastVigorCost_;
    int minimumFleeOddsThreshold_;
    int desperateFleeThreshold_;

    // Battalion type lookup vector (indexed by BattalionTypeId)
    std::vector<BattalionTypeSPtr> battalionTypes_;

    // Caches (stored as references)
    const APDCache &apdCache_;
    const ALCache &alCache_;
};

}  // namespace shardok

#endif  // EAGLE0_SHARDOK_AI_SCORE_PRIVATE_ABSTRACT_AI_SCORE_CALCULATOR_HPP
