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

#ifndef EAGLE0_ITERATIVEDEEPENINGAI_HPP
#define EAGLE0_ITERATIVEDEEPENINGAI_HPP

#include <chrono>
#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/library/ShardokCTypes.h"
#include "src/main/cpp/net/eagle0/shardok/library/action_point_distances/ActionPointDistancesCache.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"
#include "src/main/protobuf/net/eagle0/shardok/api/command_descriptor.pb.h"

namespace shardok {

// Forward declarations
class ShardokEngine;
using ScoreValue = double;
using CommandProto = net::eagle0::shardok::api::CommandDescriptor;

/// 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;
        int 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 APDCache& apdCache,
            const ALCache& alCache);

    [[nodiscard]] SearchResult IterativeSearch(
            const GameSettingsSPtr& settings,
            const GameStateW& state,
            const std::vector<CommandProto>& commands,
            const AITimeBudget& timeBudget) const;

private:
    PlayerId playerId;
    bool isDefender;
    AIStrategy strategy;
    CoordsSet castleCoords;
    const APDCache& apdCache;
    const ALCache& alCache;

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

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

    [[nodiscard]] SearchResult SearchCommandAtDepthWithEngine(
            const ShardokEngine& guessedEngine,
            const GameSettings::Getter& settingsGetter,
            int maxRepeatCount,
            const std::vector<CommandProto>& commands,
            size_t commandIndex,
            int depth,
            ScoreValue currentUtility,
            AITimeBudget& timeBudget) const;

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

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

}  // namespace shardok

#endif  // EAGLE0_ITERATIVEDEEPENINGAI_HPP