#include "game/options.hpp"

#include <SDL3/SDL.h>

#include <fstream>        // Para ifstream, ofstream
#include <iostream>       // Para cout, cerr
#include <string>         // Para string
#include <unordered_map>  // Para unordered_map

#include "core/input/input_types.hpp"  // Para BUTTON_TO_STRING, STRING_TO_BUTTON
#include "external/fkyaml_node.hpp"    // Para fkyaml::node
#include "game/defaults.hpp"           // Para GameDefaults::VERSION

namespace Options {

// --- Funciones helper de conversión ---

// Mapa de nombres de filtro
const std::unordered_map<Screen::Filter, std::string> FILTER_TO_STRING = {
    {Screen::Filter::NEAREST, "nearest"},
    {Screen::Filter::LINEAR, "linear"}};

const std::unordered_map<std::string, Screen::Filter> STRING_TO_FILTER = {
    {"nearest", Screen::Filter::NEAREST},
    {"linear", Screen::Filter::LINEAR}};

// Mapa de scancodes comunes (los más usados para controles de juego)
const std::unordered_map<SDL_Scancode, std::string> SCANCODE_TO_STRING = {
    // Flechas
    {SDL_SCANCODE_LEFT, "LEFT"},
    {SDL_SCANCODE_RIGHT, "RIGHT"},
    {SDL_SCANCODE_UP, "UP"},
    {SDL_SCANCODE_DOWN, "DOWN"},
    // Letras
    {SDL_SCANCODE_A, "A"},
    {SDL_SCANCODE_B, "B"},
    {SDL_SCANCODE_C, "C"},
    {SDL_SCANCODE_D, "D"},
    {SDL_SCANCODE_E, "E"},
    {SDL_SCANCODE_F, "F"},
    {SDL_SCANCODE_G, "G"},
    {SDL_SCANCODE_H, "H"},
    {SDL_SCANCODE_I, "I"},
    {SDL_SCANCODE_J, "J"},
    {SDL_SCANCODE_K, "K"},
    {SDL_SCANCODE_L, "L"},
    {SDL_SCANCODE_M, "M"},
    {SDL_SCANCODE_N, "N"},
    {SDL_SCANCODE_O, "O"},
    {SDL_SCANCODE_P, "P"},
    {SDL_SCANCODE_Q, "Q"},
    {SDL_SCANCODE_R, "R"},
    {SDL_SCANCODE_S, "S"},
    {SDL_SCANCODE_T, "T"},
    {SDL_SCANCODE_U, "U"},
    {SDL_SCANCODE_V, "V"},
    {SDL_SCANCODE_W, "W"},
    {SDL_SCANCODE_X, "X"},
    {SDL_SCANCODE_Y, "Y"},
    {SDL_SCANCODE_Z, "Z"},
    // Números
    {SDL_SCANCODE_0, "0"},
    {SDL_SCANCODE_1, "1"},
    {SDL_SCANCODE_2, "2"},
    {SDL_SCANCODE_3, "3"},
    {SDL_SCANCODE_4, "4"},
    {SDL_SCANCODE_5, "5"},
    {SDL_SCANCODE_6, "6"},
    {SDL_SCANCODE_7, "7"},
    {SDL_SCANCODE_8, "8"},
    {SDL_SCANCODE_9, "9"},
    // Teclas especiales
    {SDL_SCANCODE_SPACE, "SPACE"},
    {SDL_SCANCODE_RETURN, "RETURN"},
    {SDL_SCANCODE_ESCAPE, "ESCAPE"},
    {SDL_SCANCODE_TAB, "TAB"},
    {SDL_SCANCODE_BACKSPACE, "BACKSPACE"},
    {SDL_SCANCODE_LSHIFT, "LSHIFT"},
    {SDL_SCANCODE_RSHIFT, "RSHIFT"},
    {SDL_SCANCODE_LCTRL, "LCTRL"},
    {SDL_SCANCODE_RCTRL, "RCTRL"},
    {SDL_SCANCODE_LALT, "LALT"},
    {SDL_SCANCODE_RALT, "RALT"}};

const std::unordered_map<std::string, SDL_Scancode> STRING_TO_SCANCODE = {
    // Flechas
    {"LEFT", SDL_SCANCODE_LEFT},
    {"RIGHT", SDL_SCANCODE_RIGHT},
    {"UP", SDL_SCANCODE_UP},
    {"DOWN", SDL_SCANCODE_DOWN},
    // Letras
    {"A", SDL_SCANCODE_A},
    {"B", SDL_SCANCODE_B},
    {"C", SDL_SCANCODE_C},
    {"D", SDL_SCANCODE_D},
    {"E", SDL_SCANCODE_E},
    {"F", SDL_SCANCODE_F},
    {"G", SDL_SCANCODE_G},
    {"H", SDL_SCANCODE_H},
    {"I", SDL_SCANCODE_I},
    {"J", SDL_SCANCODE_J},
    {"K", SDL_SCANCODE_K},
    {"L", SDL_SCANCODE_L},
    {"M", SDL_SCANCODE_M},
    {"N", SDL_SCANCODE_N},
    {"O", SDL_SCANCODE_O},
    {"P", SDL_SCANCODE_P},
    {"Q", SDL_SCANCODE_Q},
    {"R", SDL_SCANCODE_R},
    {"S", SDL_SCANCODE_S},
    {"T", SDL_SCANCODE_T},
    {"U", SDL_SCANCODE_U},
    {"V", SDL_SCANCODE_V},
    {"W", SDL_SCANCODE_W},
    {"X", SDL_SCANCODE_X},
    {"Y", SDL_SCANCODE_Y},
    {"Z", SDL_SCANCODE_Z},
    // Números
    {"0", SDL_SCANCODE_0},
    {"1", SDL_SCANCODE_1},
    {"2", SDL_SCANCODE_2},
    {"3", SDL_SCANCODE_3},
    {"4", SDL_SCANCODE_4},
    {"5", SDL_SCANCODE_5},
    {"6", SDL_SCANCODE_6},
    {"7", SDL_SCANCODE_7},
    {"8", SDL_SCANCODE_8},
    {"9", SDL_SCANCODE_9},
    // Teclas especiales
    {"SPACE", SDL_SCANCODE_SPACE},
    {"RETURN", SDL_SCANCODE_RETURN},
    {"ESCAPE", SDL_SCANCODE_ESCAPE},
    {"TAB", SDL_SCANCODE_TAB},
    {"BACKSPACE", SDL_SCANCODE_BACKSPACE},
    {"LSHIFT", SDL_SCANCODE_LSHIFT},
    {"RSHIFT", SDL_SCANCODE_RSHIFT},
    {"LCTRL", SDL_SCANCODE_LCTRL},
    {"RCTRL", SDL_SCANCODE_RCTRL},
    {"LALT", SDL_SCANCODE_LALT},
    {"RALT", SDL_SCANCODE_RALT}};

// Mapa extendido de botones de gamepad (incluye ejes como botones)
const std::unordered_map<int, std::string> GAMEPAD_BUTTON_TO_STRING = {
    {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"},
    {SDL_GAMEPAD_BUTTON_DPAD_UP, "DPAD_UP"},
    {SDL_GAMEPAD_BUTTON_DPAD_DOWN, "DPAD_DOWN"},
    {SDL_GAMEPAD_BUTTON_DPAD_LEFT, "DPAD_LEFT"},
    {SDL_GAMEPAD_BUTTON_DPAD_RIGHT, "DPAD_RIGHT"},
    {100, "L2_AS_BUTTON"},
    {101, "R2_AS_BUTTON"},
    {200, "LEFT_STICK_LEFT"},
    {201, "LEFT_STICK_RIGHT"}};

const std::unordered_map<std::string, int> STRING_TO_GAMEPAD_BUTTON = {
    {"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},
    {"DPAD_UP", SDL_GAMEPAD_BUTTON_DPAD_UP},
    {"DPAD_DOWN", SDL_GAMEPAD_BUTTON_DPAD_DOWN},
    {"DPAD_LEFT", SDL_GAMEPAD_BUTTON_DPAD_LEFT},
    {"DPAD_RIGHT", SDL_GAMEPAD_BUTTON_DPAD_RIGHT},
    {"L2_AS_BUTTON", 100},
    {"R2_AS_BUTTON", 101},
    {"LEFT_STICK_LEFT", 200},
    {"LEFT_STICK_RIGHT", 201}};

// Lista de paletas válidas
const std::vector<std::string> VALID_PALETTES = {
    "black-and-white",
    "green-phosphor",
    "island-joy-16",
    "lost-century",
    "na16",
    "orange-screen",
    "pico-8",
    "ruzx-spectrum",
    "ruzx-spectrum-revision-2",
    "steam-lords",
    "sweetie-16",
    "sweetie-16_bona",
    "zx-spectrum",
    "zx-spectrum-adjusted",
    "zxarne-5-2"};

// Funciones helper de conversión
auto filterToString(Screen::Filter filter) -> std::string {
    auto it = FILTER_TO_STRING.find(filter);
    if (it != FILTER_TO_STRING.end()) {
        return it->second;
    }
    return "nearest";
}

auto stringToFilter(const std::string& str) -> Screen::Filter {
    auto it = STRING_TO_FILTER.find(str);
    if (it != STRING_TO_FILTER.end()) {
        return it->second;
    }
    return Defaults::Video::FILTER;
}

auto scancodeToString(SDL_Scancode scancode) -> std::string {
    auto it = SCANCODE_TO_STRING.find(scancode);
    if (it != SCANCODE_TO_STRING.end()) {
        return it->second;
    }
    // Fallback: devolver el código numérico como string
    return std::to_string(static_cast<int>(scancode));
}

auto stringToScancode(const std::string& str, SDL_Scancode default_value) -> SDL_Scancode {
    auto it = STRING_TO_SCANCODE.find(str);
    if (it != STRING_TO_SCANCODE.end()) {
        return it->second;
    }
    // Intentar parsear como número (compatibilidad hacia atrás)
    try {
        int val = std::stoi(str);
        return static_cast<SDL_Scancode>(val);
    } catch (...) {
        return default_value;
    }
}

auto gamepadButtonToString(int button) -> std::string {
    auto it = GAMEPAD_BUTTON_TO_STRING.find(button);
    if (it != GAMEPAD_BUTTON_TO_STRING.end()) {
        return it->second;
    }
    // Fallback: devolver el código numérico como string
    return std::to_string(button);
}

auto stringToGamepadButton(const std::string& str, int default_value) -> int {
    auto it = STRING_TO_GAMEPAD_BUTTON.find(str);
    if (it != STRING_TO_GAMEPAD_BUTTON.end()) {
        return it->second;
    }
    // Intentar parsear como número (compatibilidad hacia atrás)
    try {
        return std::stoi(str);
    } catch (...) {
        return default_value;
    }
}

auto isValidPalette(const std::string& palette) -> bool {
    return std::ranges::any_of(VALID_PALETTES, [&palette](const auto& valid) { return valid == palette; });
}

// --- Funciones helper para loadFromFile() ---

// Carga configuración de ventana desde YAML
void loadWindowConfigFromYaml(const fkyaml::node& yaml) {
    if (yaml.contains("window")) {
        const auto& win = yaml["window"];
        if (win.contains("zoom")) {
            try {
                int val = win["zoom"].get_value<int>();
                window.zoom = (val > 0) ? val : Defaults::Window::ZOOM;
            } catch (...) {
                window.zoom = Defaults::Window::ZOOM;
            }
        }
    }
}

// Carga configuración de borde desde YAML
void loadBorderConfigFromYaml(const fkyaml::node& border) {
    if (border.contains("enabled")) {
        try {
            video.border.enabled = border["enabled"].get_value<bool>();
        } catch (...) {
            video.border.enabled = Defaults::Border::ENABLED;
        }
    }

    if (border.contains("width")) {
        try {
            auto val = border["width"].get_value<float>();
            video.border.width = (val > 0) ? val : Defaults::Border::WIDTH;
        } catch (...) {
            video.border.width = Defaults::Border::WIDTH;
        }
    }

    if (border.contains("height")) {
        try {
            auto val = border["height"].get_value<float>();
            video.border.height = (val > 0) ? val : Defaults::Border::HEIGHT;
        } catch (...) {
            video.border.height = Defaults::Border::HEIGHT;
        }
    }
}

// Carga los campos básicos de configuración de video
void loadBasicVideoFieldsFromYaml(const fkyaml::node& vid) {
    // fullscreen (antes era "mode")
    if (vid.contains("fullscreen")) {
        try {
            video.fullscreen = vid["fullscreen"].get_value<bool>();
        } catch (...) {
            video.fullscreen = Defaults::Video::FULLSCREEN;
        }
    }

    // filter (ahora es string)
    if (vid.contains("filter")) {
        try {
            auto filter_str = vid["filter"].get_value<std::string>();
            video.filter = stringToFilter(filter_str);
        } catch (...) {
            video.filter = Defaults::Video::FILTER;
        }
    }

    if (vid.contains("shaders")) {
        try {
            video.shaders = vid["shaders"].get_value<bool>();
        } catch (...) {
            video.shaders = Defaults::Video::SHADERS;
        }
    }

    if (vid.contains("vertical_sync")) {
        try {
            video.vertical_sync = vid["vertical_sync"].get_value<bool>();
        } catch (...) {
            video.vertical_sync = Defaults::Video::VERTICAL_SYNC;
        }
    }

    if (vid.contains("integer_scale")) {
        try {
            video.integer_scale = vid["integer_scale"].get_value<bool>();
        } catch (...) {
            video.integer_scale = Defaults::Video::INTEGER_SCALE;
        }
    }

    if (vid.contains("keep_aspect")) {
        try {
            video.keep_aspect = vid["keep_aspect"].get_value<bool>();
        } catch (...) {
            video.keep_aspect = Defaults::Video::KEEP_ASPECT;
        }
    }

    if (vid.contains("palette")) {
        try {
            auto palette_str = vid["palette"].get_value<std::string>();
            if (isValidPalette(palette_str)) {
                video.palette = palette_str;
            } else {
                video.palette = Defaults::Video::PALETTE_NAME;
            }
        } catch (...) {
            video.palette = Defaults::Video::PALETTE_NAME;
        }
    }
}

// Carga configuración de video desde YAML
void loadVideoConfigFromYaml(const fkyaml::node& yaml) {
    if (yaml.contains("video")) {
        const auto& vid = yaml["video"];
        loadBasicVideoFieldsFromYaml(vid);

        // Lee border
        if (vid.contains("border")) {
            loadBorderConfigFromYaml(vid["border"]);
        }
    }
}

// Carga controles de teclado desde YAML
void loadKeyboardControlsFromYaml(const fkyaml::node& yaml) {
    if (yaml.contains("keyboard_controls")) {
        const auto& ctrl = yaml["keyboard_controls"];

        if (ctrl.contains("key_left")) {
            try {
                auto key_str = ctrl["key_left"].get_value<std::string>();
                keyboard_controls.key_left = stringToScancode(key_str, Defaults::Controls::KEY_LEFT);
            } catch (...) {
                keyboard_controls.key_left = Defaults::Controls::KEY_LEFT;
            }
        }

        if (ctrl.contains("key_right")) {
            try {
                auto key_str = ctrl["key_right"].get_value<std::string>();
                keyboard_controls.key_right = stringToScancode(key_str, Defaults::Controls::KEY_RIGHT);
            } catch (...) {
                keyboard_controls.key_right = Defaults::Controls::KEY_RIGHT;
            }
        }

        if (ctrl.contains("key_jump")) {
            try {
                auto key_str = ctrl["key_jump"].get_value<std::string>();
                keyboard_controls.key_jump = stringToScancode(key_str, Defaults::Controls::KEY_JUMP);
            } catch (...) {
                keyboard_controls.key_jump = Defaults::Controls::KEY_JUMP;
            }
        }
    }
}

// Carga controles de gamepad desde YAML
void loadGamepadControlsFromYaml(const fkyaml::node& yaml) {
    if (yaml.contains("gamepad_controls")) {
        const auto& gp = yaml["gamepad_controls"];

        if (gp.contains("button_left")) {
            try {
                auto button_str = gp["button_left"].get_value<std::string>();
                gamepad_controls.button_left = stringToGamepadButton(button_str, Defaults::Controls::GAMEPAD_BUTTON_LEFT);
            } catch (...) {
                gamepad_controls.button_left = Defaults::Controls::GAMEPAD_BUTTON_LEFT;
            }
        }

        if (gp.contains("button_right")) {
            try {
                auto button_str = gp["button_right"].get_value<std::string>();
                gamepad_controls.button_right = stringToGamepadButton(button_str, Defaults::Controls::GAMEPAD_BUTTON_RIGHT);
            } catch (...) {
                gamepad_controls.button_right = Defaults::Controls::GAMEPAD_BUTTON_RIGHT;
            }
        }

        if (gp.contains("button_jump")) {
            try {
                auto button_str = gp["button_jump"].get_value<std::string>();
                gamepad_controls.button_jump = stringToGamepadButton(button_str, Defaults::Controls::GAMEPAD_BUTTON_JUMP);
            } catch (...) {
                gamepad_controls.button_jump = Defaults::Controls::GAMEPAD_BUTTON_JUMP;
            }
        }
    }
}

// Crea e inicializa las opciones del programa
void init() {
#ifdef _DEBUG
    console = true;
#else
    console = false;
#endif
}

// Establece la ruta del fichero de configuración
void setConfigFile(const std::string& path) {
    config_file_path = path;
}

// Carga las opciones desde el fichero configurado
auto loadFromFile() -> bool {
    // Versión esperada del fichero
    const std::string CONFIG_VERSION = std::string(Project::VERSION);
    version = "";

    // Intenta abrir y leer el fichero
    std::ifstream file(config_file_path);
    if (!file.good()) {
        if (console) {
            std::cout << "Config file not found, creating default: " << config_file_path << '\n';
        }
        saveToFile();
        return true;
    }

    // Lee todo el contenido del fichero
    std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
    file.close();

    try {
        if (console) {
            std::cout << "Reading config file: " << config_file_path << '\n';
        }

        // Parsea el YAML
        auto yaml = fkyaml::node::deserialize(content);

        // Lee la versión
        if (yaml.contains("version")) {
            version = yaml["version"].get_value<std::string>();
        }

        // Si la versión no coincide, crea un fichero nuevo con valores por defecto
        if (CONFIG_VERSION != version) {
            if (console) {
                std::cout << "Config version mismatch (expected: " << CONFIG_VERSION << ", got: " << version << "), creating new config\n";
            }
            init();
            saveToFile();
            return true;
        }

        // Carga las diferentes secciones de configuración usando funciones helper
        loadWindowConfigFromYaml(yaml);
        loadVideoConfigFromYaml(yaml);
        loadKeyboardControlsFromYaml(yaml);
        loadGamepadControlsFromYaml(yaml);

        if (console) {
            std::cout << "Config file loaded successfully\n\n";
        }

        return true;

    } catch (const fkyaml::exception& e) {
        if (console) {
            std::cerr << "Error parsing YAML config: " << e.what() << '\n';
            std::cerr << "Creating new config with defaults\n";
        }
        init();
        saveToFile();
        return true;
    }
}

// Guarda las opciones al fichero configurado
auto saveToFile() -> bool {
    // Abre el fichero para escritura
    std::ofstream file(config_file_path);
    if (!file.is_open()) {
        if (console) {
            std::cerr << "Error: Unable to open file " << config_file_path << " for writing\n";
        }
        return false;
    }

    if (console) {
        std::cout << "Writing config file: " << config_file_path << '\n';
    }

    // Escribe el fichero manualmente para controlar el orden y los comentarios
    file << "# Los Pollos Hermanos - Configuration File\n";
    file << "# \n";
    file << "# This file is automatically generated and managed by the game.\n";
    file << "# Manual edits are preserved if valid.\n";
    file << "\n";

    // VERSION
    file << "# VERSION \n";
    file << "version: \"" << Project::VERSION << "\"\n";
    file << "\n";

    // WINDOW
    file << "# WINDOW\n";
    file << "window:\n";
    file << "  zoom: " << window.zoom << "\n";
    file << "\n";

    // VIDEO
    file << "# VIDEO \n";
    file << "video:\n";
    file << "  fullscreen: " << (video.fullscreen ? "true" : "false") << "\n";
    file << "  filter: " << filterToString(video.filter) << " # filter: nearest (pixel perfect) | linear (smooth)\n";
    file << "  shaders: " << (video.shaders ? "true" : "false") << "\n";
    file << "  vertical_sync: " << (video.vertical_sync ? "true" : "false") << "\n";
    file << "  integer_scale: " << (video.integer_scale ? "true" : "false") << "\n";
    file << "  keep_aspect: " << (video.keep_aspect ? "true" : "false") << "\n";
    file << "  palette: " << video.palette << "\n";
    file << "  border:\n";
    file << "    enabled: " << (video.border.enabled ? "true" : "false") << "\n";
    file << "    width: " << video.border.width << "\n";
    file << "    height: " << video.border.height << "\n";
    file << "\n";

    // KEYBOARD CONTROLS
    file << "# KEYBOARD CONTROLS\n";
    file << "keyboard_controls:\n";
    file << "  key_left: " << scancodeToString(keyboard_controls.key_left) << "\n";
    file << "  key_right: " << scancodeToString(keyboard_controls.key_right) << "\n";
    file << "  key_jump: " << scancodeToString(keyboard_controls.key_jump) << "\n";
    file << "\n";

    // GAMEPAD CONTROLS
    file << "# GAMEPAD CONTROLS\n";
    file << "gamepad_controls:\n";
    file << "  button_left: " << gamepadButtonToString(gamepad_controls.button_left) << "\n";
    file << "  button_right: " << gamepadButtonToString(gamepad_controls.button_right) << "\n";
    file << "  button_jump: " << gamepadButtonToString(gamepad_controls.button_jump) << "\n";

    file.close();

    if (console) {
        std::cout << "Config file saved successfully\n\n";
    }

    return true;
}

}  // namespace Options
