#include "game/options.hpp"

#include <SDL3/SDL.h>

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

#include "external/fkyaml_node.hpp"  // Para fkyaml::node
#include "game/defaults.hpp"         // Para GameDefaults::VERSION

namespace Options {

// 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 = GameDefaults::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;
        }

        // Lee window
        if (yaml.contains("window")) {
            const auto& win = yaml["window"];
            if (win.contains("zoom")) {
                int val = win["zoom"].get_value<int>();
                window.zoom = (val > 0) ? val : GameDefaults::WINDOW_ZOOM;
            }
        }

        // Lee video
        if (yaml.contains("video")) {
            const auto& vid = yaml["video"];

            if (vid.contains("mode")) {
                video.fullscreen = vid["mode"].get_value<bool>();
            }

            if (vid.contains("filter")) {
                int val = vid["filter"].get_value<int>();
                if (val == static_cast<int>(Screen::Filter::NEAREST) || val == static_cast<int>(Screen::Filter::LINEAR)) {
                    video.filter = static_cast<Screen::Filter>(val);
                }
            }

            if (vid.contains("shaders")) {
                video.shaders = vid["shaders"].get_value<bool>();
            }

            if (vid.contains("vertical_sync")) {
                video.vertical_sync = vid["vertical_sync"].get_value<bool>();
            }

            if (vid.contains("integer_scale")) {
                video.integer_scale = vid["integer_scale"].get_value<bool>();
            }

            if (vid.contains("keep_aspect")) {
                video.keep_aspect = vid["keep_aspect"].get_value<bool>();
            }

            if (vid.contains("palette")) {
                video.palette = vid["palette"].get_value<std::string>();
            }

            // Lee border
            if (vid.contains("border")) {
                const auto& border = vid["border"];

                if (border.contains("enabled")) {
                    video.border.enabled = border["enabled"].get_value<bool>();
                }

                if (border.contains("width")) {
                    float val = border["width"].get_value<float>();
                    video.border.width = (val > 0) ? val : GameDefaults::BORDER_WIDTH;
                }

                if (border.contains("height")) {
                    float val = border["height"].get_value<float>();
                    video.border.height = (val > 0) ? val : GameDefaults::BORDER_HEIGHT;
                }
            }
        }

        // Lee controls
        if (yaml.contains("controls")) {
            const auto& ctrl = yaml["controls"];

            if (ctrl.contains("left")) {
                int val = ctrl["left"].get_value<int>();
                controls.key_left = static_cast<SDL_Scancode>(val);
            }

            if (ctrl.contains("right")) {
                int val = ctrl["right"].get_value<int>();
                controls.key_right = static_cast<SDL_Scancode>(val);
            }

            if (ctrl.contains("jump")) {
                int val = ctrl["jump"].get_value<int>();
                controls.key_jump = static_cast<SDL_Scancode>(val);
            }
        }

        // Lee gamepad_controls
        if (yaml.contains("gamepad_controls")) {
            const auto& gp = yaml["gamepad_controls"];

            if (gp.contains("left")) {
                gamepad_controls.button_left = gp["left"].get_value<int>();
            }

            if (gp.contains("right")) {
                gamepad_controls.button_right = gp["right"].get_value<int>();
            }

            if (gp.contains("jump")) {
                gamepad_controls.button_jump = gp["jump"].get_value<int>();
            }
        }

        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 {
    // Crea el nodo YAML raíz como mapping
    fkyaml::node yaml(fkyaml::node_type::MAPPING);

    // Establece la versión
    yaml["version"] = GameDefaults::VERSION;

    // Window
    fkyaml::node window_node(fkyaml::node_type::MAPPING);
    window_node["zoom"] = window.zoom;
    yaml["window"] = window_node;

    // Video
    fkyaml::node video_node(fkyaml::node_type::MAPPING);
    video_node["mode"] = video.fullscreen;
    video_node["filter"] = static_cast<int>(video.filter);
    video_node["shaders"] = video.shaders;
    video_node["vertical_sync"] = video.vertical_sync;
    video_node["integer_scale"] = video.integer_scale;
    video_node["keep_aspect"] = video.keep_aspect;
    video_node["palette"] = video.palette;

    // Video border
    fkyaml::node border_node(fkyaml::node_type::MAPPING);
    border_node["enabled"] = video.border.enabled;
    border_node["width"] = video.border.width;
    border_node["height"] = video.border.height;
    video_node["border"] = border_node;

    yaml["video"] = video_node;

    // Controls
    fkyaml::node controls_node(fkyaml::node_type::MAPPING);
    controls_node["left"] = static_cast<int>(controls.key_left);
    controls_node["right"] = static_cast<int>(controls.key_right);
    controls_node["jump"] = static_cast<int>(controls.key_jump);
    yaml["controls"] = controls_node;

    // Gamepad controls
    fkyaml::node gamepad_node(fkyaml::node_type::MAPPING);
    gamepad_node["left"] = gamepad_controls.button_left;
    gamepad_node["right"] = gamepad_controls.button_right;
    gamepad_node["jump"] = gamepad_controls.button_jump;
    yaml["gamepad_controls"] = gamepad_node;

    // Serializa a string
    std::string yaml_content = fkyaml::node::serialize(yaml);

    // 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 encabezado con comentarios
    file << "# JailDoctor's Dilemma - Configuration File\n";
    file << "# This file is automatically generated and managed by the game\n";
    file << "#\n";
    file << "# Video filters: 0 = Nearest (pixel perfect), 1 = Linear (smooth)\n";
    file << "# SDL_Scancode values for keyboard controls (see SDL documentation)\n";
    file << "# Gamepad button values: 0-20+ = SDL_GamepadButton, 100 = L2, 101 = R2, 200+ = Axes\n";
    file << "\n";

    // Escribe el contenido YAML
    file << yaml_content;

    file.close();

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

    return true;
}

}  // namespace Options
