#include "core/input/key_config.hpp"

#include <iostream>  // Para cerr
#include <string>    // Para string
#include <utility>   // Para move
#include <vector>    // Para vector

#include "core/input/input.hpp"                // Para Input
#include "core/input/input_types.hpp"          // Para STRING_TO_ACTION
#include "core/resources/resource_helper.hpp"  // Para Resource::Helper
#include "external/fkyaml_node.hpp"            // Para fkyaml::node

// ── Singleton ────────────────────────────────────────────────────────────────

KeyConfig* KeyConfig::instance = nullptr;

void KeyConfig::init(const std::string& yaml_path) {
    instance = new KeyConfig();
    instance->load(yaml_path);
}

void KeyConfig::destroy() {
    delete instance;
    instance = nullptr;
}

auto KeyConfig::get() -> KeyConfig* { return instance; }

// ── Carga del YAML ──────────────────────────────────────────────────────────

void KeyConfig::load(const std::string& yaml_path) {
    auto file_data = Resource::Helper::loadFile(yaml_path);
    if (file_data.empty()) {
        std::cerr << "KeyConfig: Unable to load " << yaml_path << '\n';
        return;
    }

    std::string yaml_content(file_data.begin(), file_data.end());
    fkyaml::node yaml;
    try {
        yaml = fkyaml::node::deserialize(yaml_content);
    } catch (const fkyaml::exception& e) {
        std::cerr << "KeyConfig: YAML parse error: " << e.what() << '\n';
        return;
    }

    if (!yaml.contains("scopes")) { return; }

    for (const auto& scope_node : yaml["scopes"]) {
        KeyConfigScope scope;
        scope.name = scope_node["name"].get_value<std::string>();

        if (scope_node.contains("keys")) {
            for (const auto& key_node : scope_node["keys"]) {
                KeyEntry entry;
                entry.id = key_node["id"].get_value<std::string>();
                entry.display_key = key_node["key"].get_value<std::string>();
                entry.desc = key_node["desc"].get_value<std::string>();

                // Convertir el nombre SDL a keycode
                if (key_node.contains("code")) {
                    auto code = key_node["code"].get_value<std::string>();
                    entry.keycode = SDL_GetKeyFromName(code.c_str());
                    if (entry.keycode == SDLK_UNKNOWN) {
                        std::cerr << "KeyConfig: Unknown key name '" << code
                                  << "' for " << scope.name << "." << entry.id << '\n';
                    }
                }

                // InputAction opcional (para scope GLOBAL)
                if (key_node.contains("action")) {
                    entry.action = key_node["action"].get_value<std::string>();
                }

                scope.index[entry.id] = scope.entries.size();
                scope.entries.push_back(std::move(entry));
            }
        }

        scope_index_[scope.name] = scopes_.size();
        scopes_.push_back(std::move(scope));
    }

    std::cout << "KeyConfig: Loaded " << scopes_.size() << " scopes from " << yaml_path << '\n';
}

// ── Consultas ───────────────────────────────────────────────────────────────

auto KeyConfig::key(const std::string& scope, const std::string& id) const -> SDL_Keycode {
    const auto* s = getScope(scope);
    if (s == nullptr) { return SDLK_UNKNOWN; }
    auto it = s->index.find(id);
    if (it == s->index.end()) { return SDLK_UNKNOWN; }
    return s->entries[it->second].keycode;
}

void KeyConfig::applyGlobalBindings() const {
    const auto* global = getScope("GLOBAL");
    if (global == nullptr) { return; }

    for (const auto& entry : global->entries) {
        if (entry.action.empty() || entry.keycode == SDLK_UNKNOWN) { continue; }

        auto it = STRING_TO_ACTION.find(entry.action);
        if (it == STRING_TO_ACTION.end()) {
            std::cerr << "KeyConfig: Unknown action '" << entry.action << "' for " << entry.id << '\n';
            continue;
        }

        // Convertir keycode a scancode para el sistema de Input
        SDL_Scancode scancode = SDL_GetScancodeFromKey(entry.keycode, nullptr);
        if (scancode != SDL_SCANCODE_UNKNOWN) {
            Input::get()->bindKey(it->second, scancode);
        }
    }
}

auto KeyConfig::getScopes() const -> const std::vector<KeyConfigScope>& {
    return scopes_;
}

auto KeyConfig::getScope(const std::string& name) const -> const KeyConfigScope* {
    auto it = scope_index_.find(name);
    if (it == scope_index_.end()) { return nullptr; }
    return &scopes_[it->second];
}
