//
// Default implementations for MCTSGameEngine
//

#include "MCTSGameEngine.hpp"

#include <algorithm>
#include <limits>
#include <random>
#include <vector>

namespace shardok {
namespace mcts {

double MCTSGameEngine::simulateRandomPlayout(
        const MCTSGameState& state,
        MCTSPlayerId playerId,
        int maxDepth,
        MCTSSimulationPolicy policy) const {
    // Clone state once and mutate it throughout simulation for efficiency
    auto currentState = state.clone();
    int depth = 0;

    // Use thread-local random generator for thread safety
    static thread_local std::mt19937 gen(std::random_device{}());

    // Simulate until terminal or max depth
    while (!currentState->isTerminal() && depth < maxDepth) {
        auto actions = getLegalActions(*currentState);
        if (actions.empty()) { break; }

        size_t selectedIndex = 0;

        // Select action based on policy
        switch (policy) {
            case MCTSSimulationPolicy::RANDOM: {
                std::uniform_int_distribution<> dis(0, actions.size() - 1);
                selectedIndex = dis(gen);
                break;
            }

            case MCTSSimulationPolicy::FILTERED_RANDOM: {
                auto filteredIndices = filterActions(actions, *currentState);
                if (!filteredIndices.empty()) {
                    std::uniform_int_distribution<> dis(0, filteredIndices.size() - 1);
                    selectedIndex = filteredIndices[dis(gen)];
                } else {
                    // Fall back to random if no actions pass filter
                    std::uniform_int_distribution<> dis(0, actions.size() - 1);
                    selectedIndex = dis(gen);
                }
                break;
            }

            case MCTSSimulationPolicy::BEST_IMMEDIATE: {
                double bestScore = -std::numeric_limits<double>::infinity();
                for (size_t i = 0; i < actions.size(); ++i) {
                    double score = getActionScore(
                            *currentState,
                            *actions[i],
                            currentState->currentPlayerId());
                    if (score > bestScore) {
                        bestScore = score;
                        selectedIndex = i;
                    }
                }
                break;
            }

            case MCTSSimulationPolicy::WEIGHTED_BEST_IMMEDIATE: {
                // Score all actions and weight by ranking
                std::vector<std::pair<size_t, double>> scores;
                scores.reserve(actions.size());

                for (size_t i = 0; i < actions.size(); ++i) {
                    double score = getActionScore(
                            *currentState,
                            *actions[i],
                            currentState->currentPlayerId());
                    scores.emplace_back(i, score);
                }

                // Sort by score (descending)
                std::sort(scores.begin(), scores.end(), [](const auto& a, const auto& b) {
                    return a.second > b.second;
                });

                // Create weights based on ranking (1/rank)
                std::vector<double> weights;
                weights.reserve(scores.size());
                for (size_t i = 0; i < scores.size(); ++i) { weights.push_back(1.0 / (i + 1.0)); }

                // Select based on weights
                std::discrete_distribution<> dis(weights.begin(), weights.end());
                selectedIndex = scores[dis(gen)].first;
                break;
            }
        }

        // Apply selected action using mutable version for efficiency
        applyActionMutable(currentState, *actions[selectedIndex]);
        if (!currentState) {
            break;  // Failed to apply action
        }

        depth++;
    }

    // Return evaluation from original player's perspective
    return evaluateState(*currentState, playerId);
}

}  // namespace mcts
}  // namespace shardok