#pragma once
#include <fstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

#include "core/input/input_types.hpp"  // Solo incluimos los tipos compartidos
#include "external/json.hpp"

// --- Estructuras ---
struct GamepadConfig {
    std::string name;                                             // Nombre del dispositivo
    std::string path;                                             // Ruta física del dispositivo
    std::unordered_map<InputAction, SDL_GamepadButton> bindings;  // Asociación acción-botón

    GamepadConfig(std::string name, std::string path)
        : name(std::move(name)),
          path(std::move(path)),
          bindings{
              {InputAction::FIRE_LEFT, SDL_GAMEPAD_BUTTON_WEST},
              {InputAction::FIRE_CENTER, SDL_GAMEPAD_BUTTON_NORTH},
              {InputAction::FIRE_RIGHT, SDL_GAMEPAD_BUTTON_EAST},
              {InputAction::START, SDL_GAMEPAD_BUTTON_START},
              {InputAction::SERVICE, SDL_GAMEPAD_BUTTON_BACK}} {}

    // Reasigna un botón a una acción
    void rebindAction(InputAction action, SDL_GamepadButton new_button) {
        bindings[action] = new_button;
    }
};

// --- Tipos ---
using GamepadConfigs = std::vector<GamepadConfig>;  // Vector de configuraciones de gamepad

// --- Clase GamepadConfigManager: gestor de configuraciones de gamepad ---
class GamepadConfigManager {
   public:
    // --- Métodos estáticos ---
    static auto writeToJson(const GamepadConfigs& configs, const std::string& filename) -> bool {  // Escribir configuraciones a JSON
        try {
            nlohmann::json j;
            j["gamepads"] = nlohmann::json::array();

            for (const auto& config : configs) {
                nlohmann::json gamepad_json;
                gamepad_json["name"] = config.name;
                gamepad_json["path"] = config.path;
                gamepad_json["bindings"] = nlohmann::json::object();

                // Convertir bindings a JSON
                for (const auto& [action, button] : config.bindings) {
                    auto action_it = ACTION_TO_STRING.find(action);
                    auto button_it = BUTTON_TO_STRING.find(button);

                    if (action_it != ACTION_TO_STRING.end() && button_it != BUTTON_TO_STRING.end()) {
                        gamepad_json["bindings"][action_it->second] = button_it->second;
                    }
                }

                j["gamepads"].push_back(gamepad_json);
            }

            // Escribir al archivo
            std::ofstream file(filename);
            if (!file.is_open()) {
                return false;  // NOLINT(readability-simplify-boolean-expr)
            }

            file << j.dump(4);  // Formato con indentación de 4 espacios
            file.close();
            return true;

        } catch (const std::exception& e) {
            // Log del error si tienes sistema de logging
            return false;
        }
    }

    // Leer vector de GamepadConfig desde archivo JSON
    static auto readFromJson(GamepadConfigs& configs, const std::string& filename) -> bool {
        try {
            std::ifstream file(filename);
            if (!file.is_open()) {
                return false;
            }

            nlohmann::json j;
            file >> j;
            file.close();

            configs.clear();

            if (!j.contains("gamepads") || !j["gamepads"].is_array()) {
                return false;  // NOLINT(readability-simplify-boolean-expr)
            }

            for (const auto& gamepad_json : j["gamepads"]) {
                if (!gamepad_json.contains("name") || !gamepad_json.contains("bindings")) {
                    continue;  // Saltar configuraciones malformadas
                }

                // Leer el campo path si existe, si no dejarlo vacío
                std::string path = gamepad_json.contains("path") ? gamepad_json["path"].get<std::string>() : "";
                GamepadConfig config(gamepad_json["name"], path);

                // Limpiar bindings por defecto para cargar los del archivo
                config.bindings.clear();

                // Cargar bindings desde JSON
                for (const auto& [actionStr, buttonStr] : gamepad_json["bindings"].items()) {
                    auto action_it = STRING_TO_ACTION.find(actionStr);
                    auto button_it = STRING_TO_BUTTON.find(buttonStr);

                    if (action_it != STRING_TO_ACTION.end() && button_it != STRING_TO_BUTTON.end()) {
                        config.bindings[action_it->second] = button_it->second;
                    }
                }

                configs.push_back(config);
            }

            return true;

        } catch (const std::exception& e) {
            // Log del error si tienes sistema de logging
            return false;
        }
    }

    // Método auxiliar para verificar si un archivo existe
    static auto fileExists(const std::string& filename) -> bool {
        std::ifstream file(filename);
        return file.good();
    }
};