//
// Abstract MCTS AI implementation - game agnostic
//

#ifndef EAGLE0_ABSTRACT_MCTSAI_HPP
#define EAGLE0_ABSTRACT_MCTSAI_HPP

#include <chrono>
#include <memory>
#include <unordered_map>
#include <vector>

#include "MCTSAction.hpp"
#include "MCTSGameEngine.hpp"
#include "MCTSGameState.hpp"
#include "MCTSNode.hpp"
#include "MCTSTypes.hpp"

namespace shardok {
namespace mcts {

class AbstractMCTSAI {
public:
    // Search result structure
    struct SearchResult {
        size_t bestActionIndex = 0;
        double bestScore = 0.0;
        int searchDepth = 0;
        int nodesEvaluated = 0;
        std::chrono::milliseconds searchTime{0};
        bool foundWinningMove = false;
    };

    explicit AbstractMCTSAI(MCTSPlayerId playerId, MCTSConfig config = MCTSConfig{});

    // Main search interface
    [[nodiscard]] auto Search(
            const MCTSGameEngine& engine,
            const MCTSGameState& initialState,
            std::chrono::milliseconds timeLimit) const -> SearchResult;

    // Configuration
    [[nodiscard]] auto GetConfig() const -> const MCTSConfig& { return config_; }
    void SetConfig(const MCTSConfig& newConfig) { config_ = newConfig; }

    [[nodiscard]] auto FindNodeAtDepthWithHash(
            const MCTSNode* root,
            int maxDepth,
            uint64_t targetHash) -> const MCTSNode*;

private:
    MCTSPlayerId playerId_;
    MCTSConfig config_;

    // Transposition table: maps state hash -> minimum depth at which state was reached
    // Used to detect and penalize longer paths to the same game state
    // Cleared at the start of each Search() call
    mutable std::unordered_map<uint64_t, int> transpositionTable_;

    // Core MCTS algorithm
    [[nodiscard]] auto BuildMCTSTree(
            const MCTSGameEngine& engine,
            const MCTSGameState& initialState,
            std::chrono::steady_clock::time_point deadline) const -> std::unique_ptr<MCTSNode>;

    // MCTS phases
    [[nodiscard]] auto MCTSSelection(MCTSNode* root) const -> MCTSNode*;

    [[nodiscard]] auto MCTSExpansion(MCTSNode* node, const MCTSGameEngine& engine) const
            -> MCTSNode*;

    [[nodiscard]] auto MCTSSimulation(
            const MCTSGameEngine& engine,
            const MCTSGameState& state,
            MCTSPlayerId startingPlayer,
            int startingPlayerFlips = 0) const -> double;

    auto MCTSBackpropagation(MCTSNode* node, double reward, MCTSBackpropagationPolicy policy) const
            -> void;

    // Helper functions
    [[nodiscard]] auto SelectSimulationAction(
            const MCTSGameEngine& engine,
            const MCTSGameState& state,
            const std::vector<std::unique_ptr<MCTSAction>>& actions,
            bool isMaximizing) const -> size_t;

    // Logging
    static auto LogSearchResults(
            const MCTSNode* rootNode,
            const MCTSNode* bestChild,
            const SearchResult& result) -> void;

    // Debug tree dumping
    static auto DumpTreeToFile(const MCTSNode* root, const std::string& filepath) -> void;

private:
    static auto
    DumpNodeRecursive(const MCTSNode* node, std::ostream& out, int indentLevel, bool isLastChild)
            -> void;
};

}  // namespace mcts
}  // namespace shardok

#endif  // EAGLE0_ABSTRACT_MCTSAI_HPP