//
// Abstract game engine interface for MCTS
//

#ifndef EAGLE0_MCTS_GAME_ENGINE_HPP
#define EAGLE0_MCTS_GAME_ENGINE_HPP

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

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

namespace shardok {
namespace mcts {

// Abstract interface for game engines
class MCTSGameEngine {
public:
    virtual ~MCTSGameEngine() = default;

    // Apply an action to a state and return the resulting state
    [[nodiscard]] virtual std::unique_ptr<MCTSGameState> applyAction(
            const MCTSGameState& state,
            const MCTSAction& action) const = 0;

    // Apply an action to a mutable state in-place (for efficient simulation)
    // Default: clone, apply, and move the result back
    // Override this for better performance
    virtual void applyActionMutable(std::unique_ptr<MCTSGameState>& state, const MCTSAction& action)
            const {
        state = applyAction(*state, action);
    }

    // Get all legal actions for the current state
    [[nodiscard]] virtual std::vector<std::unique_ptr<MCTSAction>> getLegalActions(
            const MCTSGameState& state) const = 0;

    // Check if a state is terminal
    [[nodiscard]] virtual bool isTerminal(const MCTSGameState& state) const = 0;

    // Evaluate a state from the perspective of a player
    [[nodiscard]] virtual double evaluateState(const MCTSGameState& state, MCTSPlayerId playerId)
            const = 0;

    // Filter actions based on game-specific heuristics
    // Returns indices of actions to keep
    // Default: no filtering (return all indices)
    [[nodiscard]] virtual std::vector<size_t> filterActions(
            const std::vector<std::unique_ptr<MCTSAction>>& actions,
            const MCTSGameState& /*state*/) const {
        std::vector<size_t> indices;
        indices.reserve(actions.size());
        for (size_t i = 0; i < actions.size(); ++i) { indices.push_back(i); }
        return indices;
    }

    // Simulate a random playout from the given state
    // Default implementation uses policy to select actions
    [[nodiscard]] virtual double simulateRandomPlayout(
            const MCTSGameState& state,
            MCTSPlayerId playerId,
            int maxDepth,
            MCTSSimulationPolicy policy) const;

    // Get the immediate score of applying an action
    // Default: apply the action and evaluate the resulting state
    [[nodiscard]] virtual double getActionScore(
            const MCTSGameState& state,
            const MCTSAction& action,
            MCTSPlayerId playerId) const {
        auto newState = applyAction(state, action);
        if (!newState) { return 0.0; }
        return evaluateState(*newState, playerId);
    }

    // Check if we should stop searching (e.g., time limit, found winning move)
    [[nodiscard]] virtual bool shouldStopSearch(
            const MCTSGameState& /*state*/,
            int /*iterations*/,
            std::chrono::steady_clock::time_point /*startTime*/) const {
        // Default: no early stopping
        return false;
    }

    // Map a filtered action index back to the original unfiltered index
    // This is needed when getLegalActions() applies filtering - the returned actions
    // may be a subset of all available actions, and this maps back to the original index.
    // Default implementation: no filtering, so filtered index = original index
    [[nodiscard]] virtual size_t mapFilteredIndexToOriginal(
            size_t filteredIndex,
            const MCTSGameState& state) const {
        // Default: no filtering, index stays the same
        (void)state;  // Suppress unused parameter warning
        return filteredIndex;
    }
};

}  // namespace mcts
}  // namespace shardok

#endif  // EAGLE0_MCTS_GAME_ENGINE_HPP