#include "game/gameplay/balloon_formations.hpp"

#include <algorithm>  // Para max, min, copy
#include <array>      // Para array
#include <cctype>     // Para isdigit
#include <cstddef>    // Para size_t
#include <exception>  // Para exception
#include <fstream>    // Para basic_istream, basic_ifstream, ifstream, istringstream
#include <iterator>   // Para reverse_iterator
#include <map>        // Para map, operator==, _Rb_tree_iterator
#include <sstream>    // Para basic_istringstream
#include <string>     // Para string, char_traits, allocator, operator==, stoi, getline, operator<=>, basic_string
#include <utility>    // Para std::cmp_less

#include "core/resources/asset.hpp"   // Para Asset
#include "game/entities/balloon.hpp"  // Para Balloon
#include "utils/param.hpp"            // Para Param, ParamGame, param
#include "utils/utils.hpp"            // Para Zone, BLOCK

void BalloonFormations::initFormations() {
    // Calcular posiciones base
    const int DEFAULT_POS_Y = param.game.play_area.rect.h - BALLOON_SPAWN_HEIGHT;

    const int X3_0 = param.game.play_area.rect.x;
    const int X3_25 = param.game.play_area.first_quarter_x - (Balloon::WIDTH.at(3) / 2);
    const int X3_75 = param.game.play_area.third_quarter_x - (Balloon::WIDTH.at(3) / 2);
    const int X3_100 = param.game.play_area.rect.w - Balloon::WIDTH.at(3);

    const int X2_0 = param.game.play_area.rect.x;
    const int X2_100 = param.game.play_area.rect.w - Balloon::WIDTH.at(2);

    const int X1_0 = param.game.play_area.rect.x;
    const int X1_100 = param.game.play_area.rect.w - Balloon::WIDTH.at(1);

    const int X0_0 = param.game.play_area.rect.x;
    const int X0_50 = param.game.play_area.center_x - (Balloon::WIDTH.at(0) / 2);
    const int X0_100 = param.game.play_area.rect.w - Balloon::WIDTH.at(0);

    // Mapa de variables para reemplazar en el archivo
    std::map<std::string, float> variables = {
        {"X0_0", X0_0},
        {"X0_50", X0_50},
        {"X0_100", X0_100},
        {"X1_0", X1_0},
        {"X1_100", X1_100},
        {"X2_0", X2_0},
        {"X2_100", X2_100},
        {"X3_0", X3_0},
        {"X3_100", X3_100},
        {"X3_25", X3_25},
        {"X3_75", X3_75},
        {"DEFAULT_POS_Y", DEFAULT_POS_Y},
        {"RIGHT", Balloon::VELX_POSITIVE},
        {"LEFT", Balloon::VELX_NEGATIVE}};

    if (!loadFormationsFromFile(Asset::get()->getPath("formations.txt"), variables)) {
        // Fallback: cargar formaciones por defecto si falla la carga del archivo
        loadDefaultFormations();
    }
}

auto BalloonFormations::loadFormationsFromFile(const std::string& filename, const std::map<std::string, float>& variables) -> bool {
    std::ifstream file(filename);
    if (!file.is_open()) {
        return false;
    }

    std::string line;
    int current_formation = -1;
    std::vector<SpawnParams> current_params;

    while (std::getline(file, line)) {
        // Eliminar espacios en blanco al inicio y final
        line = trim(line);

        // Saltar líneas vacías y comentarios
        if (line.empty() || line.at(0) == '#') {
            continue;
        }

        // Verificar si es una nueva formación
        if (line.starts_with("formation:")) {
            // Guardar formación anterior si existe
            if (current_formation >= 0 && !current_params.empty()) {
                formations_.emplace_back(current_params);
            }

            // Iniciar nueva formación
            current_formation = std::stoi(line.substr(10));
            current_params.clear();
            continue;
        }

        // Procesar línea de parámetros de balloon
        if (current_formation >= 0) {
            auto params = parseBalloonLine(line, variables);
            if (params.has_value()) {
                current_params.push_back(params.value());
            }
        }
    }

    // Guardar última formación
    if (current_formation >= 0 && !current_params.empty()) {
        formations_.emplace_back(current_params);
    }

    // Crear variantes flotantes (formaciones 50-99)
    createFloaterVariants();

#ifdef _DEBUG
    // Añadir formación de prueba
    addTestFormation();
#endif

    file.close();
    return true;
}

auto BalloonFormations::parseBalloonLine(const std::string& line, const std::map<std::string, float>& variables) -> std::optional<SpawnParams> {
    std::istringstream iss(line);
    std::string token;
    std::vector<std::string> tokens;

    // Dividir por comas
    while (std::getline(iss, token, ',')) {
        tokens.push_back(trim(token));
    }

    if (tokens.size() != 7) {
        return std::nullopt;
    }

    try {
        int x = evaluateExpression(tokens.at(0), variables);
        int offset = evaluateExpression(tokens.at(1), variables);
        int y = evaluateExpression(tokens.at(2), variables);
        float vel_x = evaluateExpression(tokens.at(3), variables);

        Balloon::Type type = (tokens.at(4) == "BALLOON") ? Balloon::Type::BALLOON : Balloon::Type::FLOATER;

        Balloon::Size size;
        if (tokens.at(5) == "SMALL") {
            size = Balloon::Size::SMALL;
            offset = offset * (Balloon::WIDTH.at(0) + 1);
        } else if (tokens.at(5) == "MEDIUM") {
            size = Balloon::Size::MEDIUM;
            offset = offset * (Balloon::WIDTH.at(1) + 1);
        } else if (tokens.at(5) == "LARGE") {
            size = Balloon::Size::LARGE;
            offset = offset * (Balloon::WIDTH.at(2) + 1);
        } else if (tokens.at(5) == "EXTRALARGE") {
            size = Balloon::Size::EXTRALARGE;
            offset = offset * (Balloon::WIDTH.at(3) + 1);
        } else {
            return std::nullopt;
        }

        float creation_time = CREATION_TIME + evaluateExpression(tokens.at(6), variables);  // Base time + offset from formations.txt

        return SpawnParams(x + offset, y, vel_x, type, size, creation_time);
    } catch (const std::exception&) {
        return std::nullopt;
    }
}

auto BalloonFormations::evaluateExpression(const std::string& expr, const std::map<std::string, float>& variables) -> float {
    std::string trimmed_expr = trim(expr);

    // Si es un número directo
    if ((std::isdigit(trimmed_expr.at(0)) != 0) || (trimmed_expr.at(0) == '-' && trimmed_expr.length() > 1)) {
        return std::stof(trimmed_expr);
    }

    // Si es una variable simple
    if (variables.contains(trimmed_expr)) {
        return variables.at(trimmed_expr);
    }

    // Evaluación de expresiones simples (suma, resta, multiplicación)
    return evaluateSimpleExpression(trimmed_expr, variables);
}

auto BalloonFormations::evaluateSimpleExpression(const std::string& expr, const std::map<std::string, float>& variables) -> float {
    // Buscar operadores (+, -, *, /)
    for (size_t i = 1; i < expr.length(); ++i) {
        char op = expr.at(i);
        if (op == '+' || op == '-' || op == '*' || op == '/') {
            std::string left = trim(expr.substr(0, i));
            std::string right = trim(expr.substr(i + 1));

            int left_val = evaluateExpression(left, variables);
            int right_val = evaluateExpression(right, variables);

            switch (op) {
                case '+':
                    return left_val + right_val;
                case '-':
                    return left_val - right_val;
                case '*':
                    return left_val * right_val;
                case '/':
                    return right_val != 0 ? left_val / right_val : 0;
            }
        }
    }

    // Si no se encuentra operador, intentar como variable o número
    return variables.contains(expr) ? variables.at(expr) : std::stof(expr);
}

auto BalloonFormations::trim(const std::string& str) -> std::string {
    size_t start = str.find_first_not_of(" \t\r\n");
    if (start == std::string::npos) {
        return "";
    }
    size_t end = str.find_last_not_of(" \t\r\n");
    return str.substr(start, end - start + 1);
}

void BalloonFormations::createFloaterVariants() {
    formations_.resize(100);

    // Crear variantes flotantes de las primeras 50 formaciones
    for (size_t k = 0; k < 50 && k < formations_.size(); k++) {
        std::vector<SpawnParams> floater_params;
        floater_params.reserve(formations_.at(k).balloons.size());

        for (const auto& original : formations_.at(k).balloons) {
            floater_params.emplace_back(original.x, original.y, original.vel_x, Balloon::Type::FLOATER, original.size, original.creation_counter);
        }

        formations_.at(k + 50) = Formation(floater_params);
    }
}
#ifdef _DEBUG
void BalloonFormations::addTestFormation() {
    std::vector<SpawnParams> test_params = {
        {10, -BLOCK, 0, Balloon::Type::FLOATER, Balloon::Size::SMALL, 3.334F},         // 200 frames ÷ 60fps = 3.334s
        {50, -BLOCK, 0, Balloon::Type::FLOATER, Balloon::Size::MEDIUM, 3.334F},        // 200 frames ÷ 60fps = 3.334s
        {90, -BLOCK, 0, Balloon::Type::FLOATER, Balloon::Size::LARGE, 3.334F},         // 200 frames ÷ 60fps = 3.334s
        {140, -BLOCK, 0, Balloon::Type::FLOATER, Balloon::Size::EXTRALARGE, 3.334F}};  // 200 frames ÷ 60fps = 3.334s

    formations_.at(99) = Formation(test_params);
}
#endif
void BalloonFormations::loadDefaultFormations() {
    // Código de fallback con algunas formaciones básicas hardcodeadas
    // para que el juego funcione aunque falle la carga del archivo

    const int DEFAULT_POS_Y = param.game.play_area.rect.h - BALLOON_SPAWN_HEIGHT;
    const int X4_0 = param.game.play_area.rect.x;
    const int X4_100 = param.game.play_area.rect.w - Balloon::WIDTH.at(3);

    // Formación básica #00
    std::vector<SpawnParams> basic_formation = {
        SpawnParams(X4_0, DEFAULT_POS_Y, Balloon::VELX_POSITIVE, Balloon::Type::BALLOON, Balloon::Size::EXTRALARGE, DEFAULT_CREATION_TIME),
        SpawnParams(X4_100, DEFAULT_POS_Y, Balloon::VELX_NEGATIVE, Balloon::Type::BALLOON, Balloon::Size::EXTRALARGE, DEFAULT_CREATION_TIME)};
    formations_.emplace_back(basic_formation);
}

// Nuevas implementaciones para el sistema de pools flexible

void BalloonFormations::initFormationPools() {
    // Intentar cargar pools desde archivo
    if (!loadPoolsFromFile(Asset::get()->getPath("pools.txt"))) {
        // Fallback: cargar pools por defecto si falla la carga del archivo
        loadDefaultPools();
    }
}

auto BalloonFormations::loadPoolsFromFile(const std::string& filename) -> bool {
    std::ifstream file(filename);
    if (!file.is_open()) {
        return false;
    }

    std::string line;
    pools_.clear();  // Limpiar pools existentes

    // Map temporal para ordenar los pools por ID
    std::map<int, std::vector<int>> temp_pools;

    while (std::getline(file, line)) {
        // Eliminar espacios en blanco al inicio y final
        line = trim(line);

        // Saltar líneas vacías y comentarios
        if (line.empty() || line.at(0) == '#') {
            continue;
        }

        // Procesar línea de pool
        auto pool_data = parsePoolLine(line);
        if (pool_data.has_value()) {
            temp_pools[pool_data->first] = pool_data->second;
        }
    }

    file.close();

    // Convertir el map ordenado a vector
    // Redimensionar el vector para el pool con ID más alto
    if (!temp_pools.empty()) {
        int max_pool_id = temp_pools.rbegin()->first;
        pools_.resize(max_pool_id + 1);

        for (const auto& [pool_id, formations] : temp_pools) {
            pools_[pool_id] = formations;
        }
    }

    return !pools_.empty();
}

auto BalloonFormations::parsePoolLine(const std::string& line) -> std::optional<std::pair<int, std::vector<int>>> {
    // Formato esperado: "POOL: 0 FORMATIONS: 1, 2, 14, 3, 5, 5"

    // Buscar "POOL:"
    size_t pool_pos = line.find("POOL:");
    if (pool_pos == std::string::npos) {
        return std::nullopt;
    }

    // Buscar "FORMATIONS:"
    size_t formations_pos = line.find("FORMATIONS:");
    if (formations_pos == std::string::npos) {
        return std::nullopt;
    }

    try {
        // Extraer el ID del pool
        std::string pool_id_str = trim(line.substr(pool_pos + 5, formations_pos - pool_pos - 5));
        int pool_id = std::stoi(pool_id_str);

        // Extraer la lista de formaciones
        std::string formations_str = trim(line.substr(formations_pos + 11));
        std::vector<int> formation_ids;

        // Parsear la lista de formaciones separadas por comas
        std::istringstream iss(formations_str);
        std::string token;

        while (std::getline(iss, token, ',')) {
            token = trim(token);
            if (!token.empty()) {
                int formation_id = std::stoi(token);

                // Validar que el ID de formación existe
                if (formation_id >= 0 && std::cmp_less(formation_id, formations_.size())) {
                    formation_ids.push_back(formation_id);
                }
            }
        }

        if (!formation_ids.empty()) {
            return std::make_pair(pool_id, formation_ids);
        }
    } catch (const std::exception&) {
        // Error de conversión o parsing
        return std::nullopt;
    }

    return std::nullopt;
}

void BalloonFormations::loadDefaultPools() {
    // Pools por defecto como fallback
    pools_.clear();

    // Crear algunos pools básicos si tenemos formaciones disponibles
    if (formations_.empty()) {
        return;
    }

    size_t total_formations = formations_.size();

    // Pool 0: Primeras 10 formaciones (o las que haya disponibles)
    Pool pool0;
    for (size_t i = 0; i < std::min(static_cast<size_t>(10), total_formations); ++i) {
        pool0.push_back(static_cast<int>(i));
    }
    if (!pool0.empty()) {
        pools_.push_back(pool0);
    }

    // Pool 1: Formaciones 10-19 (si existen)
    if (total_formations > 10) {
        Pool pool1;
        for (size_t i = 10; i < std::min(static_cast<size_t>(20), total_formations); ++i) {
            pool1.push_back(static_cast<int>(i));
        }
        if (!pool1.empty()) {
            pools_.push_back(pool1);
        }
    }

    // Pool 2: Mix de formaciones normales y floaters (50+)
    if (total_formations > 50) {
        Pool pool2;
        // Agregar algunas formaciones básicas
        for (size_t i = 0; i < std::min(static_cast<size_t>(5), total_formations); ++i) {
            pool2.push_back(static_cast<int>(i));
        }
        // Agregar algunas floaters si existen
        for (size_t i = 50; i < std::min(static_cast<size_t>(55), total_formations); ++i) {
            pool2.push_back(static_cast<int>(i));
        }
        if (!pool2.empty()) {
            pools_.push_back(pool2);
        }
    }

    // Pool 3: Solo floaters (si existen formaciones 50+)
    if (total_formations > 50) {
        Pool pool3;
        for (size_t i = 50; i < std::min(static_cast<size_t>(70), total_formations); ++i) {
            pool3.push_back(static_cast<int>(i));
        }
        if (!pool3.empty()) {
            pools_.push_back(pool3);
        }
    }
}