#include "game/options.hpp"

#include <SDL3/SDL.h>

#include <filesystem>     // Para create_directories
#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
#include "utils/utils.hpp"             // Para toLower

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}};

    // 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;
        }
    }

    // --- 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;
            }
        }
    }

    // Helper: carga el campo palette; PaletteManager hará el fallback si el nombre no existe
    void loadPaletteFromYaml(const fkyaml::node& vid) {
        if (vid.contains("palette")) {
            try {
                auto palette_str = vid["palette"].get_value<std::string>();
                video.palette = toLower(palette_str);
            } catch (...) {
                video.palette = Defaults::Video::PALETTE_NAME;
            }
        }
        if (vid.contains("palette_sort")) {
            try {
                video.palette_sort = toLower(vid["palette_sort"].get_value<std::string>());
            } catch (...) {
                video.palette_sort = Defaults::Video::PALETTE_SORT;
            }
        }
    }

    // Helper: carga la sección gpu desde YAML
    void loadGPUConfigFromYaml(const fkyaml::node& gpu_node) {
        if (gpu_node.contains("acceleration")) {
            try {
                video.gpu.acceleration = gpu_node["acceleration"].get_value<bool>();
            } catch (...) {
                video.gpu.acceleration = Defaults::Video::GPU_ACCELERATION;
            }
        }
        if (gpu_node.contains("preferred_driver")) {
            try {
                video.gpu.preferred_driver = gpu_node["preferred_driver"].get_value<std::string>();
            } catch (...) {
                video.gpu.preferred_driver = "";
            }
        }
    }

    // Helper: carga la sección supersampling desde YAML
    void loadSupersamplingConfigFromYaml(const fkyaml::node& ss_node) {
        if (ss_node.contains("enabled")) {
            try {
                video.supersampling.enabled = ss_node["enabled"].get_value<bool>();
            } catch (...) {
                video.supersampling.enabled = Defaults::Video::SUPERSAMPLING;
            }
        }
        if (ss_node.contains("linear_upscale")) {
            try {
                video.supersampling.linear_upscale = ss_node["linear_upscale"].get_value<bool>();
            } catch (...) {
                video.supersampling.linear_upscale = Defaults::Video::LINEAR_UPSCALE;
            }
        }
        if (ss_node.contains("downscale_algo")) {
            try {
                video.supersampling.downscale_algo = ss_node["downscale_algo"].get_value<int>();
            } catch (...) {
                video.supersampling.downscale_algo = Defaults::Video::DOWNSCALE_ALGO;
            }
        }
    }

    // Helper: carga la sección shader desde YAML
    void loadShaderConfigFromYaml(const fkyaml::node& sh_node) {
        if (sh_node.contains("enabled")) {
            try {
                video.shader.enabled = sh_node["enabled"].get_value<bool>();
            } catch (...) {
                video.shader.enabled = Defaults::Video::SHADER_ENABLED;
            }
        }
        if (sh_node.contains("current_shader")) {
            try {
                const auto S = sh_node["current_shader"].get_value<std::string>();
                video.shader.current_shader = (S == "crtpi") ? Rendering::ShaderType::CRTPI : Rendering::ShaderType::POSTFX;
            } catch (...) {
                video.shader.current_shader = Rendering::ShaderType::POSTFX;
            }
        }
        if (sh_node.contains("current_postfx_preset")) {
            try {
                video.shader.current_postfx_preset_name = sh_node["current_postfx_preset"].get_value<std::string>();
            } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
            }
        }
        if (sh_node.contains("current_crtpi_preset")) {
            try {
                video.shader.current_crtpi_preset_name = sh_node["current_crtpi_preset"].get_value<std::string>();
            } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
            }
        }
    }

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

        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("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;
            }
        }

        loadPaletteFromYaml(vid);
    }

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

            if (vid.contains("border")) {
                loadBorderConfigFromYaml(vid["border"]);
            }
            if (vid.contains("gpu")) {
                loadGPUConfigFromYaml(vid["gpu"]);
            }
            if (vid.contains("supersampling")) {
                loadSupersamplingConfigFromYaml(vid["supersampling"]);
            }
            if (vid.contains("shader")) {
                loadShaderConfigFromYaml(vid["shader"]);
            }
        }
    }

    // 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;
                }
            }
        }
    }

    // Carga configuración del modo kiosko desde YAML
    void loadKioskConfigFromYaml(const fkyaml::node& yaml) {
        if (yaml.contains("kiosk")) {
            const auto& k = yaml["kiosk"];

            if (k.contains("enabled")) {
                try {
                    kiosk.enabled = k["enabled"].get_value<bool>();
                } catch (...) {
                    kiosk.enabled = Defaults::Kiosk::ENABLED;
                }
            }

            if (k.contains("text")) {
                try {
                    kiosk.text = k["text"].get_value<std::string>();
                } catch (...) {
                    kiosk.text = Defaults::Kiosk::TEXT;
                }
            }

            if (k.contains("infinite_lives")) {
                try {
                    kiosk.infinite_lives = k["infinite_lives"].get_value<bool>();
                } catch (...) {
                    kiosk.infinite_lives = Defaults::Kiosk::INFINITE_LIVES;
                }
            }
        }
    }

    // Carga configuración de audio desde YAML
    void loadAudioConfigFromYaml(const fkyaml::node& yaml) {  // NOLINT(readability-function-cognitive-complexity)
        if (!yaml.contains("audio")) { return; }
        const auto& a = yaml["audio"];

        if (a.contains("enabled")) {
            try {
                audio.enabled = a["enabled"].get_value<bool>();
            } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
            }
        }
        if (a.contains("volume")) {
            try {
                audio.volume = std::clamp(a["volume"].get_value<float>(), 0.0F, 1.0F);
            } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
            }
        }
        if (a.contains("music")) {
            const auto& m = a["music"];
            if (m.contains("enabled")) {
                try {
                    audio.music.enabled = m["enabled"].get_value<bool>();
                } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
                }
            }
            if (m.contains("volume")) {
                try {
                    audio.music.volume = std::clamp(m["volume"].get_value<float>(), 0.0F, 1.0F);
                } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
                }
            }
        }
        if (a.contains("sound")) {
            const auto& s = a["sound"];
            if (s.contains("enabled")) {
                try {
                    audio.sound.enabled = s["enabled"].get_value<bool>();
                } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
                }
            }
            if (s.contains("volume")) {
                try {
                    audio.sound.volume = std::clamp(s["volume"].get_value<float>(), 0.0F, 1.0F);
                } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
                }
            }
        }
    }

    // Carga configuración de la consola desde YAML
    void loadConsoleConfigFromYaml(const fkyaml::node& yaml) {  // NOLINT(readability-function-cognitive-complexity)
        if (!yaml.contains("console")) { return; }
        const auto& c = yaml["console"];

        if (c.contains("transparent")) {
            try {
                console.transparent = c["transparent"].get_value<bool>();
            } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
            }
        }
        if (c.contains("bg_color")) {
            try {
                console.bg_color = std::clamp(c["bg_color"].get_value<int>(), 0, 255);
            } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
            }
        }
        if (c.contains("msg_color")) {
            try {
                console.msg_color = std::clamp(c["msg_color"].get_value<int>(), 0, 255);
            } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
            }
        }
        if (c.contains("prompt_color")) {
            try {
                console.prompt_color = std::clamp(c["prompt_color"].get_value<int>(), 0, 255);
            } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
            }
        }
        if (c.contains("command_color")) {
            try {
                console.command_color = std::clamp(c["command_color"].get_value<int>(), 0, 255);
            } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
            }
        }
    }

    // Carga configuración de la pantalla de carga de recursos desde YAML
    void loadLoadingConfigFromYaml(const fkyaml::node& yaml) {
        if (!yaml.contains("loading")) { return; }
        const auto& l = yaml["loading"];

        if (l.contains("show_progress")) {
            try {
                loading.show_progress = l["show_progress"].get_value<bool>();
            } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
            }
        }
    }

    // Carga configuración de idioma desde YAML
    void loadLocalizationFromYaml(const fkyaml::node& yaml) {
        if (yaml.contains("localization")) {
            const auto& loc = yaml["localization"];

            if (loc.contains("language")) {
                try {
                    language = loc["language"].get_value<std::string>();
                } catch (...) {
                    language = Defaults::Localization::LANGUAGE;
                }
            }
        }
    }

    // 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 = Texts::VERSION;
        version = "";

        // Intenta abrir y leer el fichero
        std::ifstream file(config_file_path);
        if (!file.good()) {
            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 {
            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) {
                std::cout << "Config version mismatch (expected: " << CONFIG_VERSION << ", got: " << version << "), creating new config\n";
                saveToFile();
                return true;
            }

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

            std::cout << "Config file loaded successfully\n\n";

            return true;

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

    // Helper: retorna el nombre del preset PostFX actual (para guardar en config)
    auto currentPostFXPresetName() -> std::string {
        const auto IDX = static_cast<size_t>(video.shader.current_postfx_preset);
        if (IDX < postfx_presets.size()) {
            return postfx_presets[IDX].name;
        }
        // Presets no cargados aún: devolver el nombre almacenado del config
        return video.shader.current_postfx_preset_name;
    }

    // Helper: retorna el nombre del preset CrtPi actual (para guardar en config)
    auto currentCrtPiPresetName() -> std::string {
        const auto IDX = static_cast<size_t>(video.shader.current_crtpi_preset);
        if (IDX < crtpi_presets.size()) {
            return crtpi_presets[IDX].name;
        }
        return video.shader.current_crtpi_preset_name;
    }

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

        std::cout << "Writing config file: " << config_file_path << '\n';

        // Escribe el fichero manualmente para controlar el orden y los comentarios
        file << "# Projecte 2026 - 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: \"" << Texts::VERSION << "\"\n";
        file << "\n";

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

        // AUDIO
        file << "# AUDIO\n";
        file << "audio:\n";
        file << "  enabled: " << (audio.enabled ? "true" : "false") << "\n";
        file << "  volume: " << audio.volume << " # 0.0-1.0 (volumen maestro)\n";
        file << "  music:\n";
        file << "    enabled: " << (audio.music.enabled ? "true" : "false") << "\n";
        file << "    volume: " << audio.music.volume << "\n";
        file << "  sound:\n";
        file << "    enabled: " << (audio.sound.enabled ? "true" : "false") << "\n";
        file << "    volume: " << audio.sound.volume << "\n";
        file << "\n";

        // CONSOLE
        file << "# CONSOLE\n";
        file << "console:\n";
        file << "  transparent: " << (console.transparent ? "true" : "false") << " # true = sin fondo, false = solida\n";
        file << "  bg_color: " << console.bg_color << " # indice de paleta del fondo (solo si transparent: false)\n";
        file << "  msg_color: " << console.msg_color << " # indice de paleta de los mensajes\n";
        file << "  prompt_color: " << console.prompt_color << " # indice de paleta del prompt y del borde\n";
        file << "  command_color: " << console.command_color << " # indice de paleta del texto escrito por el usuario\n";
        file << "\n";

        // VIDEO
        file << "# VIDEO \n";
        file << "video:\n";
        file << "  fullscreen: " << (video.fullscreen ? "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 << "  filter: " << filterToString(video.filter) << " # filter: nearest (pixel perfect) | linear (smooth)\n";
        file << "  palette: " << video.palette << "\n";
        file << "  palette_sort: " << video.palette_sort << "\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 << "  gpu:\n";
        file << "    acceleration: " << (video.gpu.acceleration ? "true" : "false") << " # Usar aceleración hardware GPU (false = SDL fallback)\n";
        file << "    preferred_driver: \"" << video.gpu.preferred_driver << "\" # Driver GPU específico (empty = auto, aplica solo si gpu_acceleration: true)\n";
        file << "  supersampling:\n";
        file << "    enabled: " << (video.supersampling.enabled ? "true" : "false") << "\n";
        file << "    linear_upscale: " << (video.supersampling.linear_upscale ? "true" : "false") << "\n";
        file << "    downscale_algo: " << video.supersampling.downscale_algo << " # 0=bilinear, 1=Lanczos2, 2=Lanczos3\n";
        file << "  shader:\n";
        file << "    enabled: " << (video.shader.enabled ? "true" : "false") << "\n";
        file << "    current_shader: " << (video.shader.current_shader == Rendering::ShaderType::CRTPI ? "crtpi" : "postfx") << "\n";
        file << "    current_postfx_preset: " << currentPostFXPresetName() << "\n";
        file << "    current_crtpi_preset: " << currentCrtPiPresetName() << "\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";

        // KIOSK
        file << "\n";

        file << "# KIOSK MODE\n";
        file << "kiosk:\n";
        file << "  enabled: " << (kiosk.enabled ? "true" : "false") << "\n";
        file << "  text: \"" << kiosk.text << "\"\n";
        file << "  infinite_lives: " << (kiosk.infinite_lives ? "true" : "false") << "\n";

        // LOADING
        file << "\n";
        file << "# LOADING (pantalla de carga de recursos)\n";
        file << "loading:\n";
        file << "  show_progress: " << (loading.show_progress ? "true" : "false") << " # mostrar barra de progreso al cargar recursos\n";

        // LOCALIZATION
        file << "\n";
        file << "# LOCALIZATION (en = English, ca = Catalan)\n";
        file << "localization:\n";
        file << "  language: \"" << language << "\"\n";

        file.close();

        std::cout << "Config file saved successfully\n\n";

        return true;
    }

    // Resuelve el nombre del preset PostFX a índice dentro del vector de presets
    void resolvePostFXPresetName() {
        const auto& name = video.shader.current_postfx_preset_name;
        if (name.empty()) {
            video.shader.current_postfx_preset = 0;
            return;
        }
        for (int i = 0; i < static_cast<int>(postfx_presets.size()); ++i) {
            if (postfx_presets[static_cast<size_t>(i)].name == name) {
                video.shader.current_postfx_preset = i;
                return;
            }
        }
        std::cout << "PostFX preset '" << name << "' not found, defaulting to first preset\n";
        video.shader.current_postfx_preset = 0;
    }

    // Resuelve el nombre del preset CrtPi a índice dentro del vector de presets
    void resolveCrtPiPresetName() {
        const auto& name = video.shader.current_crtpi_preset_name;
        if (name.empty()) {
            video.shader.current_crtpi_preset = 0;
            return;
        }
        for (int i = 0; i < static_cast<int>(crtpi_presets.size()); ++i) {
            if (crtpi_presets[static_cast<size_t>(i)].name == name) {
                video.shader.current_crtpi_preset = i;
                return;
            }
        }
        std::cout << "CrtPi preset '" << name << "' not found, defaulting to first preset\n";
        video.shader.current_crtpi_preset = 0;
    }

    // Establece la ruta del fichero de PostFX
    void setPostFXFile(const std::string& path) {
        postfx_file_path = path;
    }

    // Helper: extrae un campo float de un nodo YAML si existe, ignorando errores de conversión
    static void parseFloatField(const fkyaml::node& node, const std::string& key, float& target) {
        if (node.contains(key)) {
            try {
                target = node[key].get_value<float>();
            } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
            }
        }
    }

    // Carga los presets de PostFX desde el fichero
    auto loadPostFXFromFile() -> bool {
        postfx_presets.clear();

        std::ifstream file(postfx_file_path);
        if (!file.good()) {
            std::cout << "PostFX file not found, creating default: " << postfx_file_path << '\n';
            return savePostFXToFile();
        }

        std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
        file.close();

        try {
            auto yaml = fkyaml::node::deserialize(content);

            if (yaml.contains("presets")) {
                const auto& presets = yaml["presets"];
                for (const auto& p : presets) {
                    PostFXPreset preset;
                    if (p.contains("name")) {
                        preset.name = p["name"].get_value<std::string>();
                    }
                    parseFloatField(p, "vignette", preset.vignette);
                    parseFloatField(p, "scanlines", preset.scanlines);
                    parseFloatField(p, "chroma", preset.chroma);
                    parseFloatField(p, "mask", preset.mask);
                    parseFloatField(p, "gamma", preset.gamma);
                    parseFloatField(p, "curvature", preset.curvature);
                    parseFloatField(p, "bleeding", preset.bleeding);
                    parseFloatField(p, "flicker", preset.flicker);
                    // Nota: 'supersampling' era un campo por-preset (eliminado). Si existe
                    // en el fichero del usuario se ignora silenciosamente (compatible).
                    postfx_presets.push_back(preset);
                }
            }

            // Resolver el nombre del preset a índice
            if (!postfx_presets.empty()) {
                resolvePostFXPresetName();
            } else {
                video.shader.current_postfx_preset = 0;
            }

            std::cout << "PostFX file loaded: " << postfx_presets.size() << " preset(s)\n";

            return true;

        } catch (const fkyaml::exception& e) {
            std::cerr << "Error parsing PostFX YAML: " << e.what() << '\n';
            return savePostFXToFile();
        }
    }

    // Guarda los presets de PostFX por defecto
    auto savePostFXToFile() -> bool {
        if (postfx_file_path.empty()) {
            return false;
        }

        std::ofstream file(postfx_file_path);
        if (!file.is_open()) {
            std::cerr << "Error: Unable to open file " << postfx_file_path << " for writing\n";
            return false;
        }

        file << "# Projecte 2026 - PostFX Presets\n";
        file << "# Each preset defines the intensity of post-processing effects (0.0 to 1.0).\n";
        file << "# vignette: screen darkening at the edges\n";
        file << "# scanlines: horizontal scanline effect\n";
        file << "# chroma: chromatic aberration (RGB color fringing)\n";
        file << "# mask: phosphor dot mask (RGB subpixel pattern)\n";
        file << "# gamma: gamma correction input 2.4 / output 2.2\n";
        file << "# curvature: CRT barrel distortion\n";
        file << "# bleeding: NTSC horizontal colour bleeding\n";
        file << "# flicker: phosphor CRT flicker ~50 Hz (0.0 = off, 1.0 = max)\n";
        file << "# Note: supersampling is a global toggle in config.yaml, not per-preset.\n";
        file << "\n";
        file << "presets:\n";
        file << "  - name: \"CRT\"\n";
        file << "    vignette: 0.6\n";
        file << "    scanlines: 0.7\n";
        file << "    chroma: 0.15\n";
        file << "    mask: 0.6\n";
        file << "    gamma: 0.8\n";
        file << "    curvature: 0.0\n";
        file << "    bleeding: 0.0\n";
        file << "    flicker: 0.0\n";
        file << "  - name: \"NTSC\"\n";
        file << "    vignette: 0.4\n";
        file << "    scanlines: 0.5\n";
        file << "    chroma: 0.2\n";
        file << "    mask: 0.4\n";
        file << "    gamma: 0.5\n";
        file << "    curvature: 0.0\n";
        file << "    bleeding: 0.6\n";
        file << "    flicker: 0.0\n";
        file << "  - name: \"CURVED\"\n";
        file << "    vignette: 0.5\n";
        file << "    scanlines: 0.6\n";
        file << "    chroma: 0.1\n";
        file << "    mask: 0.5\n";
        file << "    gamma: 0.7\n";
        file << "    curvature: 0.8\n";
        file << "    bleeding: 0.0\n";
        file << "    flicker: 0.0\n";
        file << "  - name: \"SCANLINES\"\n";
        file << "    vignette: 0.0\n";
        file << "    scanlines: 0.8\n";
        file << "    chroma: 0.0\n";
        file << "    mask: 0.0\n";
        file << "    gamma: 0.0\n";
        file << "    curvature: 0.0\n";
        file << "    bleeding: 0.0\n";
        file << "    flicker: 0.0\n";
        file << "  - name: \"SUBTLE\"\n";
        file << "    vignette: 0.3\n";
        file << "    scanlines: 0.4\n";
        file << "    chroma: 0.05\n";
        file << "    mask: 0.0\n";
        file << "    gamma: 0.3\n";
        file << "    curvature: 0.0\n";
        file << "    bleeding: 0.0\n";
        file << "    flicker: 0.0\n";
        file << "  - name: \"CRT LIVE\"\n";
        file << "    vignette: 0.5\n";
        file << "    scanlines: 0.6\n";
        file << "    chroma: 0.3\n";
        file << "    mask: 0.3\n";
        file << "    gamma: 0.4\n";
        file << "    curvature: 0.3\n";
        file << "    bleeding: 0.4\n";
        file << "    flicker: 0.8\n";

        file.close();

        std::cout << "PostFX file created with defaults: " << postfx_file_path << '\n';

        // Cargar los presets recién creados
        postfx_presets.clear();
        postfx_presets.push_back({"CRT", 0.6F, 0.7F, 0.3F, 0.6F, 0.8F, 0.0F, 0.0F, 0.0F});
        postfx_presets.push_back({"NTSC", 0.4F, 0.5F, 0.2F, 0.4F, 0.5F, 0.0F, 0.6F, 0.0F});
        postfx_presets.push_back({"CURVED", 0.5F, 0.6F, 0.1F, 0.5F, 0.7F, 0.8F, 0.0F, 0.0F});
        postfx_presets.push_back({"SCANLINES", 0.0F, 0.8F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F});
        postfx_presets.push_back({"SUBTLE", 0.3F, 0.4F, 0.05F, 0.0F, 0.3F, 0.0F, 0.0F, 0.0F});
        postfx_presets.push_back({"CRT LIVE", 0.5F, 0.6F, 0.3F, 0.3F, 0.4F, 0.3F, 0.4F, 0.8F});
        video.shader.current_postfx_preset = 0;

        return true;
    }

    void setCrtPiFile(const std::string& path) {
        crtpi_file_path = path;
    }

    // Carga los presets del shader CrtPi desde el fichero. Crea defaults si no existe.
    auto loadCrtPiFromFile() -> bool {  // NOLINT(readability-function-cognitive-complexity)
        crtpi_presets.clear();

        std::ifstream file(crtpi_file_path);
        if (!file.good()) {
            std::cout << "CrtPi file not found, creating default: " << crtpi_file_path << '\n';
            // Crear directorio padre si no existe
            const std::filesystem::path P(crtpi_file_path);
            if (P.has_parent_path()) {
                std::error_code ec;
                std::filesystem::create_directories(P.parent_path(), ec);
            }
            // Escribir defaults
            std::ofstream out(crtpi_file_path);
            if (!out.is_open()) {
                std::cerr << "Error: Cannot create CrtPi file: " << crtpi_file_path << '\n';
                // Cargar defaults en memoria aunque no se pueda escribir
                crtpi_presets.push_back({"DEFAULT", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, true, true, false, false});
                crtpi_presets.push_back({"CURVED", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, true, true, true, false});
                crtpi_presets.push_back({"SHARP", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, false, true, false, true});
                crtpi_presets.push_back({"MINIMAL", 8.0F, 0.05F, 2.0F, 2.4F, 2.2F, 1.00F, 0.0F, 0.0F, 0, true, false, false, false, false});
                video.shader.current_crtpi_preset = 0;
                return true;
            }
            out << "# Projecte 2026 - CrtPi Shader Presets\n";
            out << "# scanline_weight: ajuste gaussiano (mayor = scanlines mas estrechas, default 6.0)\n";
            out << "# scanline_gap_brightness: brillo minimo entre scanlines (0.0-1.0, default 0.12)\n";
            out << "# bloom_factor: factor de brillo para zonas iluminadas (default 3.5)\n";
            out << "# input_gamma: gamma de entrada - linealizacion (default 2.4)\n";
            out << "# output_gamma: gamma de salida - codificacion (default 2.2)\n";
            out << "# mask_brightness: brillo sub-pixeles de la mascara de fosforo (default 0.80)\n";
            out << "# curvature_x/y: distorsion barrel CRT (0.0 = plana)\n";
            out << "# mask_type: 0=ninguna, 1=verde/magenta, 2=RGB fosforo\n";
            out << "# enable_scanlines/multisample/gamma/curvature/sharper: true/false\n";
            out << "\n";
            out << "presets:\n";
            out << "  - name: \"DEFAULT\"\n";
            out << "    scanline_weight: 6.0\n";
            out << "    scanline_gap_brightness: 0.12\n";
            out << "    bloom_factor: 3.5\n";
            out << "    input_gamma: 2.4\n";
            out << "    output_gamma: 2.2\n";
            out << "    mask_brightness: 0.80\n";
            out << "    curvature_x: 0.05\n";
            out << "    curvature_y: 0.10\n";
            out << "    mask_type: 2\n";
            out << "    enable_scanlines: true\n";
            out << "    enable_multisample: true\n";
            out << "    enable_gamma: true\n";
            out << "    enable_curvature: false\n";
            out << "    enable_sharper: false\n";
            out << "  - name: \"CURVED\"\n";
            out << "    scanline_weight: 6.0\n";
            out << "    scanline_gap_brightness: 0.12\n";
            out << "    bloom_factor: 3.5\n";
            out << "    input_gamma: 2.4\n";
            out << "    output_gamma: 2.2\n";
            out << "    mask_brightness: 0.80\n";
            out << "    curvature_x: 0.05\n";
            out << "    curvature_y: 0.10\n";
            out << "    mask_type: 2\n";
            out << "    enable_scanlines: true\n";
            out << "    enable_multisample: true\n";
            out << "    enable_gamma: true\n";
            out << "    enable_curvature: true\n";
            out << "    enable_sharper: false\n";
            out << "  - name: \"SHARP\"\n";
            out << "    scanline_weight: 6.0\n";
            out << "    scanline_gap_brightness: 0.12\n";
            out << "    bloom_factor: 3.5\n";
            out << "    input_gamma: 2.4\n";
            out << "    output_gamma: 2.2\n";
            out << "    mask_brightness: 0.80\n";
            out << "    curvature_x: 0.05\n";
            out << "    curvature_y: 0.10\n";
            out << "    mask_type: 2\n";
            out << "    enable_scanlines: true\n";
            out << "    enable_multisample: false\n";
            out << "    enable_gamma: true\n";
            out << "    enable_curvature: false\n";
            out << "    enable_sharper: true\n";
            out << "  - name: \"MINIMAL\"\n";
            out << "    scanline_weight: 8.0\n";
            out << "    scanline_gap_brightness: 0.05\n";
            out << "    bloom_factor: 2.0\n";
            out << "    input_gamma: 2.4\n";
            out << "    output_gamma: 2.2\n";
            out << "    mask_brightness: 1.00\n";
            out << "    curvature_x: 0.0\n";
            out << "    curvature_y: 0.0\n";
            out << "    mask_type: 0\n";
            out << "    enable_scanlines: true\n";
            out << "    enable_multisample: false\n";
            out << "    enable_gamma: false\n";
            out << "    enable_curvature: false\n";
            out << "    enable_sharper: false\n";
            out.close();
            std::cout << "CrtPi file created with defaults: " << crtpi_file_path << '\n';
            crtpi_presets.push_back({"DEFAULT", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, true, true, false, false});
            crtpi_presets.push_back({"CURVED", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, true, true, true, false});
            crtpi_presets.push_back({"SHARP", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, false, true, false, true});
            crtpi_presets.push_back({"MINIMAL", 8.0F, 0.05F, 2.0F, 2.4F, 2.2F, 1.00F, 0.0F, 0.0F, 0, true, false, false, false, false});
            video.shader.current_crtpi_preset = 0;
            return true;
        }

        std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
        file.close();

        try {
            auto yaml = fkyaml::node::deserialize(content);

            if (yaml.contains("presets")) {
                const auto& presets = yaml["presets"];
                for (const auto& p : presets) {
                    CrtPiPreset preset;
                    if (p.contains("name")) {
                        preset.name = p["name"].get_value<std::string>();
                    }
                    parseFloatField(p, "scanline_weight", preset.scanline_weight);
                    parseFloatField(p, "scanline_gap_brightness", preset.scanline_gap_brightness);
                    parseFloatField(p, "bloom_factor", preset.bloom_factor);
                    parseFloatField(p, "input_gamma", preset.input_gamma);
                    parseFloatField(p, "output_gamma", preset.output_gamma);
                    parseFloatField(p, "mask_brightness", preset.mask_brightness);
                    parseFloatField(p, "curvature_x", preset.curvature_x);
                    parseFloatField(p, "curvature_y", preset.curvature_y);
                    if (p.contains("mask_type")) {
                        try {
                            preset.mask_type = p["mask_type"].get_value<int>();
                        } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
                        }
                    }
                    if (p.contains("enable_scanlines")) {
                        try {
                            preset.enable_scanlines = p["enable_scanlines"].get_value<bool>();
                        } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
                        }
                    }
                    if (p.contains("enable_multisample")) {
                        try {
                            preset.enable_multisample = p["enable_multisample"].get_value<bool>();
                        } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
                        }
                    }
                    if (p.contains("enable_gamma")) {
                        try {
                            preset.enable_gamma = p["enable_gamma"].get_value<bool>();
                        } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
                        }
                    }
                    if (p.contains("enable_curvature")) {
                        try {
                            preset.enable_curvature = p["enable_curvature"].get_value<bool>();
                        } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
                        }
                    }
                    if (p.contains("enable_sharper")) {
                        try {
                            preset.enable_sharper = p["enable_sharper"].get_value<bool>();
                        } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */
                        }
                    }
                    crtpi_presets.push_back(preset);
                }
            }

            // Resolver el nombre del preset a índice
            if (!crtpi_presets.empty()) {
                resolveCrtPiPresetName();
            } else {
                video.shader.current_crtpi_preset = 0;
            }

            std::cout << "CrtPi file loaded: " << crtpi_presets.size() << " preset(s)\n";
            return true;

        } catch (const fkyaml::exception& e) {
            std::cerr << "Error parsing CrtPi YAML: " << e.what() << '\n';
            // Cargar defaults en memoria en caso de error
            crtpi_presets.clear();
            crtpi_presets.push_back({"DEFAULT", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, true, true, false, false});
            video.shader.current_crtpi_preset = 0;
            return false;
        }
    }

}  // namespace Options
