//
// Shardok-specific game engine adapter for MCTS
//

#ifndef EAGLE0_SHARDOK_GAME_ENGINE_HPP
#define EAGLE0_SHARDOK_GAME_ENGINE_HPP

#include <atomic>
#include <functional>
#include <gtl/phmap.hpp>
#include <memory>
#include <vector>

#include "src/main/cpp/net/eagle0/common/mcts/abstract/MCTSGameEngine.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/library/ShardokEngine.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/action_point_distances/ActionPointDistancesCache.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/map/CoordsSet.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/settings/GameSettings.hpp"

namespace shardok {

// Forward declarations
class AICommandFilter;
class AIScoreCalculator;
class RandomGenerator;

// Use existing type definitions from the Shardok codebase
// GameSettingsSPtr and SettingsGetter are defined in GameSettings.hpp

namespace mcts {

class ShardokGameEngine : public MCTSGameEngine {
public:
    ShardokGameEngine(
            const ShardokEngine* engine,
            const AIScoreCalculator* scoreCalculator,
            const GameSettingsSPtr& gameSettings,
            const APDCache* apdCache,
            const ALCache* alCache,
            bool isDefender,
            const AIStrategy& strategy,
            const CoordsSet& castleCoords,
            const CoordsSet& criticalTileCoords);

    // MCTSGameEngine interface implementation
    [[nodiscard]] std::unique_ptr<MCTSGameState> applyAction(
            const MCTSGameState& state,
            const MCTSAction& action,
            double deterministicRoll = -1.0) const override;

    void applyActionMutable(std::unique_ptr<MCTSGameState>& state, const MCTSAction& action)
            const override;

    [[nodiscard]] std::vector<std::unique_ptr<MCTSAction>> getLegalActions(
            const MCTSGameState& state,
            MCTSPlayerId rootPlayerId,
            int currentPlayerFlips,
            int maxPlayerFlips) const override;

    [[nodiscard]] bool isTerminal(const MCTSGameState& state) const override;

    [[nodiscard]] double evaluateState(const MCTSGameState& state, MCTSPlayerId playerId)
            const override;

    [[nodiscard]] std::vector<size_t> filterActions(
            const std::vector<std::unique_ptr<MCTSAction>>& actions,
            const MCTSGameState& state) const override;

    [[nodiscard]] std::vector<double> getActionWeights(
            const std::vector<std::unique_ptr<MCTSAction>>& actions,
            const MCTSGameState& state) const override;

    [[nodiscard]] double getActionScore(
            const MCTSGameState& state,
            const MCTSAction& action,
            MCTSPlayerId playerId) const override;

    [[nodiscard]] bool shouldStopSearch(
            const MCTSGameState& state,
            int iterations,
            std::chrono::steady_clock::time_point startTime) const override;

    [[nodiscard]] size_t mapFilteredIndexToOriginal(
            size_t filteredIndex,
            const MCTSGameState& state) const override;

    [[nodiscard]] BinaryOutcomeInfo getBinaryOutcomeInfo(
            const MCTSGameState& state,
            const MCTSAction& action) const override;

    // Report transposition table statistics
    void reportCacheStatistics() const;

    // Reset cache statistics
    void resetCacheStatistics();

private:
    // Transposition table entry for caching legal actions
    // Note: We don't store command protos since the Engine already caches them
    struct LegalActionsCache {
        std::vector<size_t> filteredIndices;
        std::shared_ptr<ShardokEngine> engine;  // Engine with populated command cache
    };

    const AIScoreCalculator* scoreCalculator_;
    const GameSettingsSPtr gameSettings_;
    const APDCache* apdCache_;
    const ALCache* alCache_;
    bool isDefender_;
    AIStrategy strategy_;
    const CoordsSet castleCoords_;  // Own the data to avoid dangling references
    // Computed once to avoid 8.5% overhead per engine construction
    const CoordsSet criticalTileCoords_;  // Own the data to avoid dangling references

    // Transposition table for legal actions (shared across threads with lock-free hash map)
    // parallel_flat_hash_map provides thread-safe concurrent access without explicit locking
    // Using 8 submaps (N=8) to reduce contention with default 16 MCTS threads
    static gtl::parallel_flat_hash_map<
            uint64_t,
            LegalActionsCache,
            std::hash<uint64_t>,
            std::equal_to<uint64_t>,
            std::allocator<std::pair<const uint64_t, LegalActionsCache>>,
            8,
            std::mutex>
            legalActionsCache_;
    static std::atomic<uint64_t> cacheHits_;
    static std::atomic<uint64_t> cacheMisses_;

    // Performance timing (in microseconds)
    static std::atomic<uint64_t> timeInHashComputation_;
    static std::atomic<uint64_t> timeInLegalActionsComputation_;

public:
    // Clear the static legal actions cache (useful for tests)
    static void clearLegalActionsCache();
};

}  // namespace mcts
}  // namespace shardok

#endif  // EAGLE0_SHARDOK_GAME_ENGINE_HPP