#include "../include/MeteoriteFactory.h"
#include "../include/Meteorite.h"
#include "warfactory/ResourceRegistry.h"
#include "warfactory/RandomGenerator.h"
#include <fstream>
#include <algorithm>
#include <cmath>

namespace warfactory {

MeteoriteFactory::MeteoriteFactory() {
}

// ========================================
// FEED PHASE - Configuration
// ========================================

void MeteoriteFactory::setDefaultSizeBias(const std::string& size_bias) {
    default_size_bias = size_bias;
}

void MeteoriteFactory::loadMeteoriteTypes(const std::string& json_path) {
    std::ifstream file(json_path);
    if (!file.is_open()) {
        return; // TODO: Error handling
    }

    json meteorites_data;
    file >> meteorites_data;

    if (meteorites_data.contains("meteorite_types")) {
        const json& types = meteorites_data["meteorite_types"];

        // Clear existing templates
        meteorite_templates.clear();

        // Generate templates for each type
        for (json::const_iterator it = types.begin(); it != types.end(); ++it) {
            const std::string& type_name = it.key();
            json meteorite_data = it.value();
            meteorite_data["type"] = type_name;

            // Create template meteorite
            meteorite_templates[type_name] = Meteorite(meteorite_data);
        }
    }
}

void MeteoriteFactory::setTypeWeights(const std::map<std::string, float>& weights) {
    type_weights = weights;
}

std::vector<std::string> MeteoriteFactory::getAvailableTypes() const {
    std::vector<std::string> types;
    types.reserve(meteorite_templates.size());

    for (const std::pair<const std::string, Meteorite>& pair : meteorite_templates) {
        types.push_back(pair.first);
    }

    std::sort(types.begin(), types.end());
    return types;
}

// ========================================
// PULL PHASE - Generation
// ========================================

std::unique_ptr<Meteorite> MeteoriteFactory::getProceduralMeteorite() {
    if (!isConfigured()) {
        return nullptr;
    }

    const std::string selected_type = selectRandomType();
    return createSpecificMeteorite(selected_type);
}

std::unique_ptr<Meteorite> MeteoriteFactory::getProceduralMeteorite(const std::string& size_override) {
    if (!isConfigured()) {
        return nullptr;
    }

    const std::string selected_type = selectRandomType();
    const std::map<std::string, Meteorite>::const_iterator it = meteorite_templates.find(selected_type);
    if (it == meteorite_templates.end()) {
        return nullptr;
    }

    // Copy template and apply size modifications
    std::unique_ptr<Meteorite> meteorite = std::make_unique<Meteorite>(it->second.copy());
    applySizeModifications(*meteorite, size_override);
    return meteorite;
}

std::unique_ptr<Meteorite> MeteoriteFactory::createSpecificMeteorite(const std::string& meteorite_type) {
    const std::map<std::string, json>::const_iterator it = meteorite_types.find(meteorite_type);
    if (it == meteorite_types.end()) {
        return nullptr;
    }

    const json& meteorite_template = it->second;
    const std::string size_bias = global_config.value("size_bias", "medium");

    // Build complete meteorite data
    json meteorite_data = meteorite_template;
    meteorite_data["type"] = meteorite_type;
    meteorite_data["mass"] = generateMass(meteorite_template, size_bias);

    // Convert composition percentages to actual quantities based on mass
    if (meteorite_data.contains("composition")) {
        const float total_mass = meteorite_data["mass"].get<float>();
        json& composition = meteorite_data["composition"];

        for (json::iterator comp_it = composition.begin(); comp_it != composition.end(); ++comp_it) {
            const float percentage = comp_it.value().get<float>();
            const float quantity = (total_mass * percentage) / 100.0f;
            comp_it.value() = quantity;
        }
    }

    return std::make_unique<Meteorite>(meteorite_data);
}

// ========================================
// UTILITY
// ========================================

bool MeteoriteFactory::isConfigured() const {
    return !meteorite_templates.empty() && !type_weights.empty();
}

void MeteoriteFactory::reset() {
    meteorite_templates.clear();
    type_weights.clear();
    default_size_bias = "medium";
}

float MeteoriteFactory::getTotalWeight() const {
    float total = 0.0f;
    for (const std::pair<const std::string, float>& pair : type_weights) {
        total += pair.second;
    }
    return total;
}

// ========================================
// PRIVATE METHODS
// ========================================

std::string MeteoriteFactory::selectRandomType() const {
    const float total_weight = getTotalWeight();
    if (total_weight <= 0.0f) {
        return meteorite_templates.empty() ? "" : meteorite_templates.begin()->first;
    }

    const float random_value = RandomGenerator::getInstance().uniform(0.0f, total_weight);

    float cumulative_weight = 0.0f;
    for (const std::pair<const std::string, float>& pair : type_weights) {
        cumulative_weight += pair.second;
        if (random_value <= cumulative_weight) {
            return pair.first;
        }
    }

    // Fallback to last type
    return type_weights.empty() ? "" : type_weights.rbegin()->first;
}

void MeteoriteFactory::applySizeModifications(Meteorite& meteorite, const std::string& size_bias) const {
    // Get current mass and apply size bias
    const float current_mass = meteorite.getMass();

    // Define mass range based on meteorite type (could be stored in template data)
    const float min_mass = current_mass * 0.5f;  // 50% to 200% of base mass
    const float max_mass = current_mass * 2.0f;

    const float new_mass = applySizeBias(min_mass, max_mass, size_bias);
    meteorite.setMass(new_mass);

    // TODO: Scale resource composition based on new mass
    // This would require updating resource quantities proportionally
}

float MeteoriteFactory::applySizeBias(float min_mass, float max_mass, const std::string& size_bias) const {
    const float random_factor = RandomGenerator::getInstance().unit();

    if (size_bias == "small") {
        // Bias toward smaller masses (power curve favoring lower values)
        const float biased_factor = std::pow(random_factor, 2.0f);
        return min_mass + (max_mass - min_mass) * biased_factor;
    } else if (size_bias == "large" || size_bias == "larger") {
        // Bias toward larger masses (inverse power curve)
        const float biased_factor = 1.0f - std::pow(1.0f - random_factor, 2.0f);
        return min_mass + (max_mass - min_mass) * biased_factor;
    } else if (size_bias == "massive") {
        // Heavily bias toward maximum masses
        const float biased_factor = 1.0f - std::pow(1.0f - random_factor, 4.0f);
        return min_mass + (max_mass - min_mass) * biased_factor;
    } else {
        // "medium" or unknown - uniform distribution
        return min_mass + (max_mass - min_mass) * random_factor;
    }
}

} // namespace warfactory