#include "core/locale/lang.hpp"

#include <cstddef>        // Para size_t
#include <exception>      // Para exception
#include <fstream>        // Para basic_ifstream, basic_istream, ifstream
#include <unordered_map>  // Para unordered_map, _Node_iterator, operator==
#include <utility>        // Para pair
#include <vector>         // Para vector

#include "core/resources/asset.hpp"            // Para Asset
#include "core/resources/resource_helper.hpp"  // Para ResourceHelper
#include "external/json.hpp"                   // Para basic_json, iteration_proxy_value, oper...
#include "game/gameplay/difficulty.hpp"        // Para Difficulty
#include "game/options.hpp"                    // Para SettingsOpt...

using json = nlohmann::json;

namespace Lang {
    std::unordered_map<std::string, std::string> texts;

    // Vector con los idiomas soportados
    std::vector<Language> languages = {
        {Code::SPANISH, "Castellano", "es_ES.json"},
        {Code::VALENCIAN, "Balooncia", "ba_BA.json"},
        {Code::ENGLISH, "Ingles", "en_UK.json"}};

    // Inicializa los textos del juego en el idioma seleccionado
    auto loadFromFile(const std::string& file_path) -> bool {
        texts.clear();

        // Intentar cargar desde ResourceHelper primero
        auto resource_data = ResourceHelper::loadFile(file_path);

        try {
            json j;

            if (!resource_data.empty()) {
                // Cargar desde datos del pack
                std::string content(resource_data.begin(), resource_data.end());
                j = json::parse(content);
            } else {
                // Fallback a filesystem directo
                std::ifstream rfile(file_path);
                if (!rfile.is_open()) {
                    return false;
                }
                rfile >> j;
            }

            for (const auto& el : j.items()) {
                texts[el.key()] = el.value();
            }
        } catch (const std::exception& e) {
            // Puedes loguear el error si quieres
            return false;
        }

        return true;
    }

    // Obtiene el texto por clave
    auto getText(const std::string& key) -> std::string {
        auto it = texts.find(key);
        if (it != texts.end()) {
            return it->second;
        }
        return "[missing text: " + key + "]";
    }

    // Obtiene el código del siguiente idioma disponible
    auto getNextLangCode(Code lang) -> Code {
        for (size_t i = 0; i < languages.size(); ++i) {
            if (languages[i].code == lang) {
                return languages[(i + 1) % languages.size()].code;
            }
        }
        // Si no se encuentra, devuelve el primero por defecto
        return languages[0].code;
    }

    // Obtiene un idioma del vector de idiomas a partir de un código
    auto getLanguage(Code code) -> Language {
        for (const auto& lang : languages) {
            if (lang.code == code) {
                return lang;
            }
        }
        // Si no se encuentra, devuelve el primero por defecto
        return languages[0];
    }

    // Devuelve el código de un idioma a partir de un nombre
    auto getCodeFromName(const std::string& name) -> Code {
        for (const auto& lang : languages) {
            if (lang.name == name) {
                return lang.code;
            }
        }
        // Si no se encuentra, devuelve el primero por defecto
        return languages[0].code;
    }

    // Devuelve el nombre de un idioma a partir de un código
    auto getNameFromCode(Code code) -> std::string {
        for (const auto& lang : languages) {
            if (lang.code == code) {
                return lang.name;
            }
        }
        // Si no se encuentra, devuelve el nombre del primer idioma por defecto
        return languages[0].name;
    }

    // Actualiza los nombres de los idiomas
    void updateLanguageNames() {
        for (auto& lang : languages) {
            switch (lang.code) {
                case Code::SPANISH:
                    lang.name = Lang::getText("[SERVICE_MENU] LANG_ES");
                    break;
                case Code::VALENCIAN:
                    lang.name = Lang::getText("[SERVICE_MENU] LANG_BA");
                    break;
                case Code::ENGLISH:
                    lang.name = Lang::getText("[SERVICE_MENU] LANG_EN");
                    break;
                default:
                    lang.name = "Unknown";
                    break;
            }
        }
    }

    // Actualiza los nombres de las dificultades
    void updateDifficultyNames() {
        // 1. Pide una referencia MODIFICABLE a la lista de dificultades
        auto& difficulties = Difficulty::getDifficulties();

        // 2. Recorre la lista
        for (auto& difficulty_info : difficulties) {
            // 3. Para cada dificultad, usa su código para obtener el texto traducido y actualizar su nombre
            switch (difficulty_info.code) {
                case Difficulty::Code::EASY:
                    difficulty_info.name = Lang::getText("[SERVICE_MENU] EASY");
                    break;
                case Difficulty::Code::NORMAL:
                    difficulty_info.name = Lang::getText("[SERVICE_MENU] NORMAL");
                    break;
                case Difficulty::Code::HARD:
                    difficulty_info.name = Lang::getText("[SERVICE_MENU] HARD");
                    break;
            }
        }
    }

    // Obtiene una fichero a partir de un lang::Code
    auto getLanguageFileName(Lang::Code code) -> std::string {
        for (const auto& lang : languages) {
            if (lang.code == code) {
                return Asset::get()->getPath(lang.file_name);
            }
        }
        // Si no se encuentra, devuelve el fichero del primer idioma por defecto
        return Asset::get()->getPath(languages[0].file_name);
    }

    // Establece el idioma
    void setLanguage(Code code) {
        Options::settings.language = code;
        loadFromFile(Asset::get()->getPath(getLanguage(code).file_name));
        updateLanguageNames();
        updateDifficultyNames();
    }

    // Obtiene una fichero a partir de un Code
    auto getLangFile(Code code) -> std::string {
        switch (code) {
            case Code::VALENCIAN:
                return Asset::get()->getPath("ba_BA.json");
                break;
            case Code::SPANISH:
                return Asset::get()->getPath("es_ES.json");
                break;
            default:
                return Asset::get()->getPath("en_UK.json");
                break;
        }
    }

    // Obtiene una cadena a partir de un Code
    auto getLangName(Code code) -> std::string {
        switch (code) {
            case Code::VALENCIAN:
                return " \"ba_BA\"";
                break;
            case Code::SPANISH:
                return " \"es_ES\"";
                break;
            default:
                return " \"en_UK\"";
                break;
        }
    }
}  // namespace Lang
