//
// Created by Dan Crosby on 07/04/25.
//

#ifndef EAGLE0_ITERATIVEDEEPENINGAI_HPP
#define EAGLE0_ITERATIVEDEEPENINGAI_HPP

#include <chrono>
#include <future>
#include <vector>

#include "AIStrategy.hpp"
#include "AITimeBudget.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai/AIAttackLocations.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai/score/AIScoreCalculator.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/ShardokCTypes.h"
#include "src/main/cpp/net/eagle0/shardok/library/ShardokCommand.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/action_point_distances/ActionPointDistancesCache.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"

namespace shardok {

// Forward declarations
class ShardokEngine;
using ScoreValue = double;
using BattalionTypeGetter = std::function<BattalionTypeSPtr(BattalionTypeId)>;

/// Reason why AI evaluation completed at the achieved depth.
enum class EvaluationCompletionReason {
    RAN_OUT_OF_COMMANDS,         ///< All remaining commands were trivial (e.g., END_TURN)
    RAN_OUT_OF_TIME,             ///< Time budget was exhausted with meaningful commands remaining
    NOT_ENOUGH_TIME_TO_CONTINUE  ///< Insufficient time budget to start next depth iteration
};

class IterativeDeepeningAI {
public:
    struct SearchResult {
        size_t bestCommandIndex;
        ScoreValue bestScore;
        size_t depthAchieved;
        std::chrono::milliseconds timeUsed;
        bool minimumDepthCompleted;
        bool searchCompleted;
        size_t availableCommandCount;
        size_t commandCountEvaluated;
        EvaluationCompletionReason completionReason;

        SearchResult()
            : bestCommandIndex(0),
              bestScore(0),
              depthAchieved(0),
              timeUsed(0),
              minimumDepthCompleted(false),
              searchCompleted(false),
              availableCommandCount(0),
              commandCountEvaluated(0),
              completionReason(EvaluationCompletionReason::RAN_OUT_OF_TIME) {}
    };

    IterativeDeepeningAI(
            PlayerId playerId,
            bool isDefender,
            AIStrategy strategy,
            const CoordsSet& castleCoords,
            const AIScoreCalculator& scorer,
            const APDCache& apdCache,
            BattalionTypeGetter battalionTypeGetter);  // Pass by value

    [[nodiscard]] SearchResult IterativeSearch(
            const GameSettingsSPtr& settings,
            const GameStateW& state,
            const CommandListSPtr& commands,
            const AITimeBudget& initialBudget) const;

private:
    PlayerId playerId;
    bool isDefender;
    AIStrategy strategy;
    CoordsSet castleCoords;
    const AIScoreCalculator& scorer;
    const APDCache& apdCache;
    BattalionTypeGetter battalionTypeGetter;  // Store by value, not reference!

    // Reusable vectors to reduce memory allocations
    mutable std::vector<std::vector<ScoreValue>> scoresByDepth;
    mutable std::vector<size_t> highestDepthCompleted;
    mutable std::vector<size_t> reusableSortedIndices;

    [[nodiscard]] static bool IsTimeExpired(const AITimeBudget& budget);

    [[nodiscard]] std::future<SearchResult> SearchCommandAtDepthWithEngine(
            const ShardokEngine& guessedEngine,
            const AIScoreCalculator& scorer,
            int maxRepeatCount,
            const CommandListSPtr& commands,
            size_t commandIndex,
            int desiredDepth,
            ScoreValue currentUtility,
            AITimeBudget& timeBudget) const;

    [[nodiscard]] static std::vector<size_t> GetCommandsSortedByPreviousDepth(
            size_t currentDepth,
            const std::vector<std::vector<ScoreValue>>& scoresByDepth,
            const std::vector<size_t>& highestDepthCompleted,
            const std::vector<size_t>& filteredIndices);

    [[nodiscard]] static SearchResult SelectBestResult(
            const std::vector<std::vector<ScoreValue>>& scoresByDepth,
            const std::vector<size_t>& highestDepthCompleted);
};

}  // namespace shardok

#endif  // EAGLE0_ITERATIVEDEEPENINGAI_HPP