#pragma once
#include <external/json.hpp>

#include <fstream>
#include <string>
#include <unordered_map>
#include <vector>

#include "input.h"  // Para Input

// Mapas para convertir entre enums y strings
std::unordered_map<Input::Action, std::string> actionToString = {
    {Input::Action::FIRE_LEFT, "FIRE_LEFT"},
    {Input::Action::FIRE_CENTER, "FIRE_CENTER"},
    {Input::Action::FIRE_RIGHT, "FIRE_RIGHT"},
    {Input::Action::START, "START"},
    {Input::Action::SERVICE, "SERVICE"}};

std::unordered_map<std::string, Input::Action> stringToAction = {
    {"FIRE_LEFT", Input::Action::FIRE_LEFT},
    {"FIRE_CENTER", Input::Action::FIRE_CENTER},
    {"FIRE_RIGHT", Input::Action::FIRE_RIGHT},
    {"START", Input::Action::START},
    {"SERVICE", Input::Action::SERVICE}};

std::unordered_map<SDL_GamepadButton, std::string> buttonToString = {
    {SDL_GAMEPAD_BUTTON_WEST, "WEST"},
    {SDL_GAMEPAD_BUTTON_NORTH, "NORTH"},
    {SDL_GAMEPAD_BUTTON_EAST, "EAST"},
    {SDL_GAMEPAD_BUTTON_SOUTH, "SOUTH"},
    {SDL_GAMEPAD_BUTTON_START, "START"},
    {SDL_GAMEPAD_BUTTON_BACK, "BACK"},
    {SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, "LEFT_SHOULDER"},
    {SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, "RIGHT_SHOULDER"}
    // Añade todos los botones que necesites
};

std::unordered_map<std::string, SDL_GamepadButton> stringToButton = {
    {"WEST", SDL_GAMEPAD_BUTTON_WEST},
    {"NORTH", SDL_GAMEPAD_BUTTON_NORTH},
    {"EAST", SDL_GAMEPAD_BUTTON_EAST},
    {"SOUTH", SDL_GAMEPAD_BUTTON_SOUTH},
    {"START", SDL_GAMEPAD_BUTTON_START},
    {"BACK", SDL_GAMEPAD_BUTTON_BACK},
    {"LEFT_SHOULDER", SDL_GAMEPAD_BUTTON_LEFT_SHOULDER},
    {"RIGHT_SHOULDER", SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER}
    // Añade todos los botones que necesites
};

struct GamepadConfig {
        std::string name;                                               // Nombre del dispositivo
        std::unordered_map<Input::Action, SDL_GamepadButton> bindings;  // Asociación acción-botón

        GamepadConfig(std::string name = "")
            : name(name),
              bindings{
                  {Input::Action::FIRE_LEFT, SDL_GAMEPAD_BUTTON_WEST},
                  {Input::Action::FIRE_CENTER, SDL_GAMEPAD_BUTTON_NORTH},
                  {Input::Action::FIRE_RIGHT, SDL_GAMEPAD_BUTTON_EAST},
                  {Input::Action::START, SDL_GAMEPAD_BUTTON_START},
                  {Input::Action::SERVICE, SDL_GAMEPAD_BUTTON_BACK}} {}

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

class GamepadConfigManager {
    public:
        // Escribir vector de GamepadConfig a archivo JSON
        static bool writeToJson(const std::vector<GamepadConfig>& configs, const std::string& filename) {
            try {
                nlohmann::json j;
                j["gamepads"] = nlohmann::json::array();

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

                    // Convertir bindings a JSON
                    for (const auto& [action, button] : config.bindings) {
                        std::string actionStr = actionToString[action];
                        std::string buttonStr = buttonToString[button];
                        gamepadJson["bindings"][actionStr] = buttonStr;
                    }

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

                // Escribir al archivo
                std::ofstream file(filename);
                if (!file.is_open()) {
                    return false;
                }

                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 bool readFromJson(std::vector<GamepadConfig>& configs, const std::string& filename) {
            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;
                }

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

                    GamepadConfig config(gamepadJson["name"]);

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

                    // Cargar bindings desde JSON
                    for (const auto& [actionStr, buttonStr] : gamepadJson["bindings"].items()) {
                        auto actionIt = stringToAction.find(actionStr);
                        auto buttonIt = stringToButton.find(buttonStr);

                        if (actionIt != stringToAction.end() && buttonIt != stringToButton.end()) {
                            config.bindings[actionIt->second] = buttonIt->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 bool fileExists(const std::string& filename) {
            std::ifstream file(filename);
            return file.good();
        }
};

/*
// Ejemplo de uso
void ejemploUso() {
    std::vector<GamepadConfig> configs;

    // Crear algunas configuraciones de ejemplo
    GamepadConfig config1("Xbox Controller");
    config1.rebindAction(Input::Action::FIRE_LEFT, SDL_GAMEPAD_BUTTON_SOUTH);

    GamepadConfig config2("PS4 Controller");
    config2.rebindAction(Input::Action::START, SDL_GAMEPAD_BUTTON_OPTIONS);

    configs.push_back(config1);
    configs.push_back(config2);

    // Escribir a archivo
    if (GamepadConfigManager::writeToJson(configs, "gamepad_config.json")) {
        std::cout << "Configuración guardada exitosamente" << std::endl;
    }

    // Leer desde archivo
    std::vector<GamepadConfig> loadedConfigs;
    if (GamepadConfigManager::readFromJson(loadedConfigs, "gamepad_config.json")) {
        std::cout << "Configuración cargada exitosamente" << std::endl;
        std::cout << "Número de configuraciones: " << loadedConfigs.size() << std::endl;
    }
}
*/