#include "utils/param.hpp"

#include <SDL3/SDL.h>  // Para SDL_LogWarn, SDL_LogCategory, SDL_LogError

#include <fstream>        // Para basic_istream, basic_ifstream, ifstream, istringstream
#include <functional>     // Para function
#include <iostream>       // Para std::cout
#include <sstream>        // Para basic_istringstream
#include <stdexcept>      // Para runtime_error
#include <string>         // Para string, basic_string, stoi, stof, hash, allocator, operator==, char_traits, operator+, operator>>, getline
#include <unordered_map>  // Para unordered_map, operator==, _Node_iterator_base
#include <utility>        // Para pair

#include "game/ui/notifier.hpp"  // Para Notifier
#include "utils/color.hpp"       // Para Color
#include "utils/utils.hpp"       // Para Zone, stringToBool, getFileName

// Variable global - ahora se inicializa automáticamente con valores por defecto
Param param;

// Declaraciones de funciones privadas
namespace {
    auto setParams(const std::string& var, const std::string& value) -> bool;
}

// Implementación del método privado de Param
void Param::precalculateZones() {
    // playArea - cálculos basados en el rectángulo actual
    game.play_area.center_x = game.play_area.rect.w / 2;
    game.play_area.first_quarter_x = game.play_area.rect.w / 4;
    game.play_area.third_quarter_x = game.play_area.rect.w / 4 * 3;
    game.play_area.center_y = game.play_area.rect.h / 2;
    game.play_area.first_quarter_y = game.play_area.rect.h / 4;
    game.play_area.third_quarter_y = game.play_area.rect.h / 4 * 3;

    // gameArea - cálculos basados en width y height actuales
    game.game_area.rect = {.x = 0, .y = 0, .w = game.width, .h = game.height};
    game.game_area.center_x = game.game_area.rect.w / 2;
    game.game_area.first_quarter_x = game.game_area.rect.w / 4;
    game.game_area.third_quarter_x = game.game_area.rect.w / 4 * 3;
    game.game_area.center_y = game.game_area.rect.h / 2;
    game.game_area.first_quarter_y = game.game_area.rect.h / 4;
    game.game_area.third_quarter_y = game.game_area.rect.h / 4 * 3;
}

// Carga los parámetros desde un archivo
void loadParamsFromFile(const std::string& file_path) {
    // Los parámetros ya están inicializados con valores por defecto
    // Solo necesitamos abrir el archivo y sobrescribir los valores que aparezcan

    std::ifstream file(file_path);
    if (!file.is_open()) {
        std::cout << "Error: No se pudo abrir el archivo " << file_path << '\n';
        throw std::runtime_error("No se pudo abrir el archivo: " + file_path);
    }

    std::string line;
    std::string param_name;
    std::string param_value;

    while (std::getline(file, line)) {
        // Elimina comentarios
        auto comment_pos = line.find('#');
        if (comment_pos != std::string::npos) {
            line.resize(comment_pos);
        }

        // Usa un stream para separar palabras
        std::istringstream iss(line);
        if (iss >> param_name >> param_value) {
            if (!setParams(param_name, param_value)) {
                std::cout << "WARNING: Parámetro desconocido: " << param_name << '\n';
            }
        }
    }

    file.close();

    // Recalcula las zonas después de cargar todos los parámetros
    param.precalculateZones();
}

// Implementación local de setParams
namespace {
    auto setParams(const std::string& var, const std::string& value) -> bool {
        // Mapas estáticos para diferentes tipos de parámetros
        static const std::unordered_map<std::string, std::function<void(const std::string&)>> INT_PARAMS = {
            {"game.width", [](const std::string& v) -> void { param.game.width = std::stoi(v); }},
            {"game.height", [](const std::string& v) -> void { param.game.height = std::stoi(v); }},
            {"game.play_area.rect.x", [](const std::string& v) -> void { param.game.play_area.rect.x = std::stoi(v); }},
            {"game.play_area.rect.y", [](const std::string& v) -> void { param.game.play_area.rect.y = std::stoi(v); }},
            {"game.play_area.rect.w", [](const std::string& v) -> void { param.game.play_area.rect.w = std::stoi(v); }},
            {"game.play_area.rect.h", [](const std::string& v) -> void { param.game.play_area.rect.h = std::stoi(v); }},
            {"game.name_entry_idle_time", [](const std::string& v) -> void { param.game.name_entry_idle_time = std::stoi(v); }},
            {"game.name_entry_total_time", [](const std::string& v) -> void { param.game.name_entry_total_time = std::stoi(v); }},
            {"fade.num_squares_width", [](const std::string& v) -> void { param.fade.num_squares_width = std::stoi(v); }},
            {"fade.num_squares_height", [](const std::string& v) -> void { param.fade.num_squares_height = std::stoi(v); }},
            {"fade.random_squares_duration_ms", [](const std::string& v) -> void { param.fade.random_squares_duration_ms = std::stoi(v); }},
            {"fade.post_duration_ms", [](const std::string& v) -> void { param.fade.post_duration_ms = std::stoi(v); }},
            {"fade.venetian_size", [](const std::string& v) -> void { param.fade.venetian_size = std::stoi(v); }},
            {"scoreboard.rect.x", [](const std::string& v) -> void { param.scoreboard.rect.x = std::stoi(v); }},
            {"scoreboard.rect.y", [](const std::string& v) -> void { param.scoreboard.rect.y = std::stoi(v); }},
            {"scoreboard.rect.w", [](const std::string& v) -> void { param.scoreboard.rect.w = std::stoi(v); }},
            {"scoreboard.rect.h", [](const std::string& v) -> void { param.scoreboard.rect.h = std::stoi(v); }},
            {"scoreboard.skip_countdown_value", [](const std::string& v) -> void { param.scoreboard.skip_countdown_value = std::stoi(v); }},
            {"title.press_start_position", [](const std::string& v) -> void { param.title.press_start_position = std::stoi(v); }},
            {"title.arcade_edition_position", [](const std::string& v) -> void { param.title.arcade_edition_position = std::stoi(v); }},
            {"title.title_c_c_position", [](const std::string& v) -> void { param.title.title_c_c_position = std::stoi(v); }},
            {"intro.text_distance_from_bottom", [](const std::string& v) -> void { param.intro.text_distance_from_bottom = std::stoi(v); }}};

        static const std::unordered_map<std::string, std::function<void(const std::string&)>> COLOR_PARAMS = {
            {"fade.color", [](const std::string& v) -> void { param.fade.color = Color::fromHex(v); }},
            {"scoreboard.separator_color", [](const std::string& v) -> void { param.scoreboard.separator_color = Color::fromHex(v); }},
            {"scoreboard.easy_color", [](const std::string& v) -> void { param.scoreboard.easy_color = Color::fromHex(v); }},
            {"scoreboard.normal_color", [](const std::string& v) -> void { param.scoreboard.normal_color = Color::fromHex(v); }},
            {"scoreboard.hard_color", [](const std::string& v) -> void { param.scoreboard.hard_color = Color::fromHex(v); }},
            {"scoreboard.text_color1", [](const std::string& v) -> void { param.scoreboard.text_color1 = Color::fromHex(v); }},
            {"scoreboard.text_color2", [](const std::string& v) -> void { param.scoreboard.text_color2 = Color::fromHex(v); }},
            {"title.bg_color", [](const std::string& v) -> void { param.title.bg_color = Color::fromHex(v); }},
            {"background.attenuate_color", [](const std::string& v) -> void { param.background.attenuate_color = Color::fromHex(v); }},
            {"notification.color", [](const std::string& v) -> void { param.notification.color = Color::fromHex(v); }},
            {"service_menu.title_color", [](const std::string& v) -> void { param.service_menu.title_color = Color::fromHex(v); }},
            {"service_menu.text_color", [](const std::string& v) -> void { param.service_menu.text_color = Color::fromHex(v); }},
            {"service_menu.selected_color", [](const std::string& v) -> void { param.service_menu.selected_color = Color::fromHex(v); }},
            {"service_menu.bg_color", [](const std::string& v) -> void { param.service_menu.bg_color = Color::fromHex(v); }},
            {"service_menu.window_message.bg_color", [](const std::string& v) -> void { param.service_menu.window_message.bg_color = Color::fromHex(v); }},
            {"service_menu.window_message.border_color", [](const std::string& v) -> void { param.service_menu.window_message.border_color = Color::fromHex(v); }},
            {"service_menu.window_message.title_color", [](const std::string& v) -> void { param.service_menu.window_message.title_color = Color::fromHex(v); }},
            {"service_menu.window_message.text_color", [](const std::string& v) -> void { param.service_menu.window_message.text_color = Color::fromHex(v); }},
            {"intro.bg_color", [](const std::string& v) -> void { param.intro.bg_color = Color::fromHex(v); }},
            {"intro.card_color", [](const std::string& v) -> void { param.intro.card_color = Color::fromHex(v); }},
            {"intro.shadow_color", [](const std::string& v) -> void { param.intro.shadow_color = Color::fromHex(v); }},
            {"debug.color", [](const std::string& v) -> void { param.debug.color = Color::fromHex(v); }},
            {"resource.color", [](const std::string& v) -> void { param.resource.color = Color::fromHex(v); }},
            {"game.item_text_outline_color", [](const std::string& v) -> void { param.game.item_text_outline_color = Color::fromHex(v); }},
            {"player.default_shirt[0].darkest", [](const std::string& v) -> void { param.player.default_shirt[0].darkest = Color::fromHex(v); }},
            {"player.default_shirt[0].dark", [](const std::string& v) -> void { param.player.default_shirt[0].dark = Color::fromHex(v); }},
            {"player.default_shirt[0].base", [](const std::string& v) -> void { param.player.default_shirt[0].base = Color::fromHex(v); }},
            {"player.default_shirt[0].light", [](const std::string& v) -> void { param.player.default_shirt[0].light = Color::fromHex(v); }},
            {"player.default_shirt[1].darkest", [](const std::string& v) -> void { param.player.default_shirt[1].darkest = Color::fromHex(v); }},
            {"player.default_shirt[1].dark", [](const std::string& v) -> void { param.player.default_shirt[1].dark = Color::fromHex(v); }},
            {"player.default_shirt[1].base", [](const std::string& v) -> void { param.player.default_shirt[1].base = Color::fromHex(v); }},
            {"player.default_shirt[1].light", [](const std::string& v) -> void { param.player.default_shirt[1].light = Color::fromHex(v); }},
            {"player.one_coffee_shirt[0].darkest", [](const std::string& v) -> void { param.player.one_coffee_shirt[0].darkest = Color::fromHex(v); }},
            {"player.one_coffee_shirt[0].dark", [](const std::string& v) -> void { param.player.one_coffee_shirt[0].dark = Color::fromHex(v); }},
            {"player.one_coffee_shirt[0].base", [](const std::string& v) -> void { param.player.one_coffee_shirt[0].base = Color::fromHex(v); }},
            {"player.one_coffee_shirt[0].light", [](const std::string& v) -> void { param.player.one_coffee_shirt[0].light = Color::fromHex(v); }},
            {"player.one_coffee_shirt[1].darkest", [](const std::string& v) -> void { param.player.one_coffee_shirt[1].darkest = Color::fromHex(v); }},
            {"player.one_coffee_shirt[1].dark", [](const std::string& v) -> void { param.player.one_coffee_shirt[1].dark = Color::fromHex(v); }},
            {"player.one_coffee_shirt[1].base", [](const std::string& v) -> void { param.player.one_coffee_shirt[1].base = Color::fromHex(v); }},
            {"player.one_coffee_shirt[1].light", [](const std::string& v) -> void { param.player.one_coffee_shirt[1].light = Color::fromHex(v); }},
            {"player.two_coffee_shirt[0].darkest", [](const std::string& v) -> void { param.player.two_coffee_shirt[0].darkest = Color::fromHex(v); }},
            {"player.two_coffee_shirt[0].dark", [](const std::string& v) -> void { param.player.two_coffee_shirt[0].dark = Color::fromHex(v); }},
            {"player.two_coffee_shirt[0].base", [](const std::string& v) -> void { param.player.two_coffee_shirt[0].base = Color::fromHex(v); }},
            {"player.two_coffee_shirt[0].light", [](const std::string& v) -> void { param.player.two_coffee_shirt[0].light = Color::fromHex(v); }},
            {"player.two_coffee_shirt[1].darkest", [](const std::string& v) -> void { param.player.two_coffee_shirt[1].darkest = Color::fromHex(v); }},
            {"player.two_coffee_shirt[1].dark", [](const std::string& v) -> void { param.player.two_coffee_shirt[1].dark = Color::fromHex(v); }},
            {"player.two_coffee_shirt[1].base", [](const std::string& v) -> void { param.player.two_coffee_shirt[1].base = Color::fromHex(v); }},
            {"player.two_coffee_shirt[1].light", [](const std::string& v) -> void { param.player.two_coffee_shirt[1].light = Color::fromHex(v); }},
            {"player.outline_color[0]", [](const std::string& v) -> void { param.player.outline_color[0] = Color::fromHex(v); }},
            {"player.outline_color[1]", [](const std::string& v) -> void { param.player.outline_color[1] = Color::fromHex(v); }}};

        static const std::unordered_map<std::string, std::function<void(const std::string&)>> BOOL_PARAMS = {
            {"scoreboard.separator_autocolor", [](const std::string& v) -> void { param.scoreboard.separator_autocolor = stringToBool(v); }},
            {"scoreboard.text_autocolor", [](const std::string& v) -> void { param.scoreboard.text_autocolor = stringToBool(v); }},
            {"balloon.bouncing_sound", [](const std::string& v) -> void { param.balloon.bouncing_sound = stringToBool(v); }},
            {"notification.sound", [](const std::string& v) -> void { param.notification.sound = stringToBool(v); }},
            {"service_menu.drop_shadow", [](const std::string& v) -> void { param.service_menu.drop_shadow = stringToBool(v); }}};

        static const std::unordered_map<std::string, std::function<void(const std::string&)>> FLOAT_PARAMS = {
            {"balloon.settings[0].vel", [](const std::string& v) -> void { param.balloon.settings.at(0).vel = std::stof(v); }},
            {"balloon.settings[0].grav", [](const std::string& v) -> void { param.balloon.settings.at(0).grav = std::stof(v); }},
            {"balloon.settings[1].vel", [](const std::string& v) -> void { param.balloon.settings.at(1).vel = std::stof(v); }},
            {"balloon.settings[1].grav", [](const std::string& v) -> void { param.balloon.settings.at(1).grav = std::stof(v); }},
            {"balloon.settings[2].vel", [](const std::string& v) -> void { param.balloon.settings.at(2).vel = std::stof(v); }},
            {"balloon.settings[2].grav", [](const std::string& v) -> void { param.balloon.settings.at(2).grav = std::stof(v); }},
            {"balloon.settings[3].vel", [](const std::string& v) -> void { param.balloon.settings.at(3).vel = std::stof(v); }},
            {"balloon.settings[3].grav", [](const std::string& v) -> void { param.balloon.settings.at(3).grav = std::stof(v); }},
            {"tabe.min_spawn_time", [](const std::string& v) -> void { param.tabe.min_spawn_time = std::stof(v); }},
            {"tabe.max_spawn_time", [](const std::string& v) -> void { param.tabe.max_spawn_time = std::stof(v); }},
            {"title.title_duration", [](const std::string& v) -> void { param.title.title_duration = std::stof(v); }},
            {"service_menu.window_message.padding", [](const std::string& v) -> void { param.service_menu.window_message.padding = std::stof(v); }},
            {"service_menu.window_message.line_spacing", [](const std::string& v) -> void { param.service_menu.window_message.line_spacing = std::stof(v); }},
            {"service_menu.window_message.title_separator_spacing", [](const std::string& v) -> void { param.service_menu.window_message.title_separator_spacing = std::stof(v); }},
            {"service_menu.window_message.min_width", [](const std::string& v) -> void { param.service_menu.window_message.min_width = std::stof(v); }},
            {"service_menu.window_message.min_height", [](const std::string& v) -> void { param.service_menu.window_message.min_height = std::stof(v); }},
            {"service_menu.window_message.max_width_ratio", [](const std::string& v) -> void { param.service_menu.window_message.max_width_ratio = std::stof(v); }},
            {"service_menu.window_message.max_height_ratio", [](const std::string& v) -> void { param.service_menu.window_message.max_height_ratio = std::stof(v); }},
            {"service_menu.window_message.text_safety_margin", [](const std::string& v) -> void { param.service_menu.window_message.text_safety_margin = std::stof(v); }},
            {"service_menu.window_message.animation_duration", [](const std::string& v) -> void { param.service_menu.window_message.animation_duration = std::stof(v); }}};

        static const std::unordered_map<std::string, std::function<void(const std::string&)>> INT_PARAMS_EXTRA = {};

        // Colores válidos para globos
        static const std::unordered_map<std::string, bool> VALID_BALLOON_COLORS = {
            {"blue", true},
            {"orange", true},
            {"red", true},
            {"green", true}};

        auto validate_balloon_color = [](const std::string& color) -> bool {
            return VALID_BALLOON_COLORS.contains(color);
        };

        static const std::unordered_map<std::string, std::function<void(const std::string&)>> STRING_PARAMS = {
            {"balloon.color[0]", [validate_balloon_color](const std::string& v) -> void {
                 if (!validate_balloon_color(v)) {
                     std::cout << "Color de globo inválido '" << v << "'. Usando 'blue' por defecto." << '\n';
                     param.balloon.color.at(0) = "blue";
                 } else {
                     param.balloon.color.at(0) = v;
                 }
             }},
            {"balloon.color[1]", [validate_balloon_color](const std::string& v) -> void {
                 if (!validate_balloon_color(v)) {
                     std::cout << "Color de globo inválido '" << v << "'. Usando 'orange' por defecto." << '\n';
                     param.balloon.color.at(1) = "orange";
                 } else {
                     param.balloon.color.at(1) = v;
                 }
             }},
            {"balloon.color[2]", [validate_balloon_color](const std::string& v) -> void {
                 if (!validate_balloon_color(v)) {
                     std::cout << "Color de globo inválido '" << v << "'. Usando 'red' por defecto." << '\n';
                     param.balloon.color.at(2) = "red";
                 } else {
                     param.balloon.color.at(2) = v;
                 }
             }},
            {"balloon.color[3]", [validate_balloon_color](const std::string& v) -> void {
                 if (!validate_balloon_color(v)) {
                     std::cout << "Color de globo inválido '" << v << "'. Usando 'green' por defecto." << '\n';
                     param.balloon.color.at(3) = "green";
                 } else {
                     param.balloon.color.at(3) = v;
                 }
             }}};

        // Lambda para intentar cada mapa de parámetros
        auto try_map = [&](const auto& param_map) -> bool {
            auto it = param_map.find(var);
            if (it != param_map.end()) {
                it->second(value);
                return true;
            }
            return false;
        };

        // Intentar con todos los mapas
        if (try_map(INT_PARAMS) || try_map(COLOR_PARAMS) || try_map(BOOL_PARAMS) ||
            try_map(FLOAT_PARAMS) || try_map(STRING_PARAMS)) {
            return true;
        }

        // Casos especiales que necesitan lógica personalizada
        if (var == "notification.pos_h") {
            if (value == "LEFT") {
                param.notification.pos_h = Notifier::Position::LEFT;
            } else if (value == "MIDDLE") {
                param.notification.pos_h = Notifier::Position::MIDDLE;
            } else {
                param.notification.pos_h = Notifier::Position::RIGHT;
            }
            return true;
        }

        if (var == "notification.pos_v") {
            param.notification.pos_v = value == "TOP" ? Notifier::Position::TOP : Notifier::Position::BOTTOM;
            return true;
        }

        return false;  // Parámetro no encontrado
    }
}  // namespace