//
// AI Battle Simulator Configuration Loader Implementation
//

#include "AiBattleConfig.hpp"

#include <google/protobuf/util/json_util.h>

#include <fstream>
#include <iostream>
#include <random>
#include <sstream>

namespace shardok {
namespace ai_battle_simulator {

using net::eagle0::shardok::ai_battle_simulator::AIAlgorithmType;
using net::eagle0::shardok::ai_battle_simulator::PlayerConfig;
using net::eagle0::shardok::ai_battle_simulator::UnitConfig;

std::unique_ptr<BattleConfigProto> AiBattleConfigLoader::LoadFromJsonFile(
        const std::string& filepath) {
    std::ifstream file(filepath);
    if (!file.is_open()) {
        std::cerr << "Failed to open config file: " << filepath << std::endl;
        return nullptr;
    }

    std::stringstream buffer;
    buffer << file.rdbuf();
    return LoadFromJsonString(buffer.str());
}

std::unique_ptr<BattleConfigProto> AiBattleConfigLoader::LoadFromJsonString(
        const std::string& jsonString) {
    auto config = std::make_unique<BattleConfigProto>();

    google::protobuf::util::JsonParseOptions options;
    options.ignore_unknown_fields = false;

    const auto status =
            google::protobuf::util::JsonStringToMessage(jsonString, config.get(), options);

    if (!status.ok()) {
        std::cerr << "Failed to parse JSON config: " << status.message() << std::endl;
        return nullptr;
    }

    return config;
}

std::unique_ptr<BattleConfigProto> AiBattleConfigLoader::CreateDefaultPerfConfig(int month) {
    auto config = std::make_unique<BattleConfigProto>();

    // Basic setup matching Unity's "Perf" configuration
    config->set_map_name("Alah");
    config->set_month(month);
    config->set_max_rounds(40);
    config->set_random_seed(0);  // Use system random

    // Attacker configuration - 6 Longbowmen units with professions 1-6
    PlayerConfig* attacker = config->mutable_attacker();
    attacker->set_ai_algorithm(AIAlgorithmType::ITERATIVE_DEEPENING);

    for (int profession = 1; profession <= 6; ++profession) {
        UnitConfig* unit = attacker->add_units();
        unit->set_profession(profession);
        unit->set_battalion_type_id(4);             // Longbowmen (battalion type 4)
        unit->set_starting_position_index(0);       // Attackers use position 0
        unit->mutable_battalion()->set_size(1000);  // Match client battalion size
    }

    // Defender configuration - 6 Light Infantry units with NO_PROFESSION (profession 14)
    PlayerConfig* defender = config->mutable_defender();
    defender->set_ai_algorithm(AIAlgorithmType::ITERATIVE_DEEPENING);

    for (int i = 0; i < 6; ++i) {
        UnitConfig* unit = defender->add_units();
        unit->set_profession(14);                   // NO_PROFESSION to match client battles
        unit->set_battalion_type_id(0);             // Light Infantry (battalion type 0)
        unit->set_starting_position_index(-1);      // Defenders use position -1
        unit->mutable_battalion()->set_size(1000);  // Match client battalion size
    }

    return config;
}

std::unique_ptr<BattleConfigProto> AiBattleConfigLoader::CreateRandomizedBattleConfig() {
    // Thread-local random number generator for thread safety
    static thread_local std::random_device rd;
    static thread_local std::mt19937 gen(rd());

    auto config = std::make_unique<BattleConfigProto>();

    // Fixed map (Alah) for consistent training
    config->set_map_name("Alah");

    // Randomize month (1-12)
    std::uniform_int_distribution<> monthDist(1, 12);
    config->set_month(monthDist(gen));

    // Max rounds and random seed
    config->set_max_rounds(40);
    config->set_random_seed(0);  // Use system random

    // Randomize number of units per side (1-10)
    std::uniform_int_distribution<> unitCountDist(1, 10);
    const int attackerUnitCount = unitCountDist(gen);
    const int defenderUnitCount = unitCountDist(gen);

    // Battalion types: 0-4 (excluding Undead which is type 5)
    // 0=Light Infantry, 1=Heavy Infantry, 2=Light Cavalry, 3=Heavy Cavalry, 4=Longbowmen
    std::uniform_int_distribution<> battalionTypeDist(0, 4);

    // Troop counts: 500-2000
    std::uniform_int_distribution<> troopCountDist(500, 2000);

    // Profession distribution: 70% hero (1-6), 30% no profession (14)
    std::uniform_int_distribution<> heroProfessionDist(1, 6);
    std::uniform_real_distribution<> hasHeroDist(0.0, 1.0);

    // Attacker starting position: 80% all same, 20% varied
    const bool varyAttackerPositions = (hasHeroDist(gen) < 0.2);
    std::uniform_int_distribution<> positionDist(0, 5);
    const int commonAttackerPosition = positionDist(gen);

    // Configure attacker
    PlayerConfig* attacker = config->mutable_attacker();
    attacker->set_ai_algorithm(AIAlgorithmType::ITERATIVE_DEEPENING);

    for (int i = 0; i < attackerUnitCount; ++i) {
        UnitConfig* unit = attacker->add_units();

        // Randomize profession (70% hero, 30% no profession)
        if (hasHeroDist(gen) < 0.7) {
            unit->set_profession(heroProfessionDist(gen));
        } else {
            unit->set_profession(14);  // NO_PROFESSION
        }

        unit->set_battalion_type_id(battalionTypeDist(gen));
        unit->mutable_battalion()->set_size(troopCountDist(gen));

        // Starting position: usually all same, occasionally varied
        if (varyAttackerPositions) {
            unit->set_starting_position_index(positionDist(gen));
        } else {
            unit->set_starting_position_index(commonAttackerPosition);
        }
    }

    // Configure defender
    PlayerConfig* defender = config->mutable_defender();
    defender->set_ai_algorithm(AIAlgorithmType::ITERATIVE_DEEPENING);

    for (int i = 0; i < defenderUnitCount; ++i) {
        UnitConfig* unit = defender->add_units();

        // Randomize profession (70% hero, 30% no profession)
        if (hasHeroDist(gen) < 0.7) {
            unit->set_profession(heroProfessionDist(gen));
        } else {
            unit->set_profession(14);  // NO_PROFESSION
        }

        unit->set_battalion_type_id(battalionTypeDist(gen));
        unit->mutable_battalion()->set_size(troopCountDist(gen));

        // Defenders always use position -1
        unit->set_starting_position_index(-1);
    }

    return config;
}

std::string AiBattleConfigLoader::ToJsonString(const BattleConfigProto& config) {
    std::string jsonString;

    google::protobuf::util::JsonPrintOptions options;
    options.add_whitespace = true;
    options.always_print_fields_with_no_presence = true;
    options.preserve_proto_field_names = true;

    const auto status = google::protobuf::util::MessageToJsonString(config, &jsonString, options);

    if (!status.ok()) {
        std::cerr << "Failed to convert config to JSON: " << status.message() << std::endl;
        return "";
    }

    return jsonString;
}

}  // namespace ai_battle_simulator
}  // namespace shardok
