#include "presentation/localization/UiLocalizationService.h"

#include "presentation/localization/LocaleMetadata.h"

#include <algorithm>
#include <cctype>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <utility>

#include "presentation/localization/LocaleRepository.h"
#include "foundation/diagnostics/BuildOptions.h"
#include "foundation/diagnostics/Logger.h"
#include "foundation/text/StringUtils.h"

namespace ta::presentation::localization
{
    namespace
    {

        struct LocalizationState
        {
            std::string selected_id;
            TranslationMap translations;
            TranslationMap english_fallback;
            std::uint64_t revision = 0;
            bool initialized = false;
            bool attempted = false;
        };

        std::shared_mutex g_mutex;
        LocalizationState g_state;
        std::vector<LocaleOption> g_options;
        std::unique_ptr<LocaleRepository> g_repository;
        std::string g_system_locale = "en";

        const LocaleOption* option_for_id_locked(const std::string_view id)
        {
            const auto found = std::find_if(g_options.begin(), g_options.end(), [&](const LocaleOption& option)
            {
                return option.id == id;
            });
            return found == g_options.end() ? nullptr : &*found;
        }

        std::string detect_locale_id_locked()
        {
            const std::string& system = g_system_locale;
            for (const LocaleOption& option : g_options)
            {
                for (const std::string& alias : option.system_aliases)
                {
                    if (system == alias || system.rfind(alias + "-", 0) == 0 || alias.rfind(system + "-", 0) == 0)
                        return option.id;
                }
            }
            return option_for_id_locked("en") ? "en" : g_options.front().id;
        }

        TranslationMap load_file_locked(const std::string& file_name)
        {
            if (!g_repository)
                return {};
            TranslationMap result = g_repository->load(file_name);
            result.erase(locale_metadata::locale_id);
            result.erase(locale_metadata::locale_name);
            result.erase(locale_metadata::locale_aliases);
            return result;
        }

        bool apply_locale_locked(std::string id)
        {
            if (!g_repository || g_options.empty())
                return false;
            if (id.empty() || id == "auto" || !option_for_id_locked(id))
                id = detect_locale_id_locked();

            const LocaleOption* option = option_for_id_locked(id);
            if (!option)
                return false;

            const LocaleOption* english_option = option_for_id_locked("en");
            TranslationMap english = english_option
                ? load_file_locked(english_option->file_name)
                : TranslationMap{};
            TranslationMap selected = option->id == "en"
                ? english
                : load_file_locked(option->file_name);

            g_state.selected_id = option->id;
            g_state.attempted = true;
            if (selected.empty() && english.empty())
            {
                g_state.translations.clear();
                g_state.english_fallback.clear();
                ++g_state.revision;
                g_state.initialized = false;
                ta::foundation::logger::error("RESOURCE", "localization unavailable");
                return false;
            }

            if (selected.empty() && option->id != "en")
            {
                ta::foundation::logger::warn(
                    "RESOURCE",
                    "localization fallback | selected=" + option->id + " fallback=en");
            }

            g_state.translations = std::move(selected);
            g_state.english_fallback = option->id == "en"
                ? TranslationMap{}
                : std::move(english);
            ++g_state.revision;
            g_state.initialized = true;
            if (ta::foundation::diagnostics::build_options::verbose)
                ta::foundation::logger::info("RESOURCE", "localization loaded | selected=" + option->id +
                             " file=" + option->file_name);
            return true;
        }
    }

    bool initialize(const std::filesystem::path& locale_directory,
                    std::string system_locale_tag)
    {
        std::unique_lock lock(g_mutex);
        g_system_locale = ta::foundation::text::lower_copy(std::move(system_locale_tag));
        if (g_system_locale.empty())
            g_system_locale = "en";
        g_repository = std::make_unique<LocaleRepository>(locale_directory);
        g_options = g_repository->discover();
        g_state.attempted = false;
        return apply_locale_locked(g_state.selected_id);
    }

    void reset() noexcept
    {
        std::unique_lock lock(g_mutex);
        g_repository.reset();
        g_options.clear();
        g_system_locale = "en";
        g_state = {};
    }

    bool set_locale(const std::string_view locale_id)
    {
        std::unique_lock lock(g_mutex);
        std::string normalized(locale_id);
        if (normalized.empty() || normalized == "auto" || !option_for_id_locked(normalized))
            normalized = detect_locale_id_locked();
        if (g_state.attempted && g_state.selected_id == normalized)
            return g_state.initialized;
        return apply_locale_locked(std::move(normalized));
    }


    std::vector<std::string> locale_option_labels()
    {
        std::shared_lock lock(g_mutex);
        std::vector<std::string> labels;
        labels.reserve(g_options.size());
        for (const LocaleOption& option : g_options)
            labels.push_back(option.label);
        return labels;
    }

    int locale_index(const std::string_view locale_id)
    {
        std::shared_lock lock(g_mutex);
        if (g_options.empty())
            return -1;
        const std::string resolved = (locale_id.empty() || locale_id == "auto")
            ? detect_locale_id_locked()
            : std::string(locale_id);
        for (std::size_t index = 0; index < g_options.size(); ++index)
        {
            if (g_options[index].id == resolved)
                return static_cast<int>(index);
        }
        return -1;
    }

    std::string locale_id_for_index(const int index)
    {
        std::shared_lock lock(g_mutex);
        if (g_options.empty())
            return "en";
        if (index < 0 || index >= static_cast<int>(g_options.size()))
            return detect_locale_id_locked();
        return g_options[static_cast<std::size_t>(index)].id;
    }

    std::string current_locale_id()
    {
        std::shared_lock lock(g_mutex);
        return g_state.selected_id.empty() ? "en" : g_state.selected_id;
    }

    std::uint64_t revision()
    {
        std::shared_lock lock(g_mutex);
        return g_state.revision;
    }

    std::string tr(const std::string& key)
    {
        std::shared_lock lock(g_mutex);
        if (const auto found = g_state.translations.find(key);
            found != g_state.translations.end() && !found->second.empty())
        {
            return found->second;
        }

        if (const auto fallback = g_state.english_fallback.find(key);
            fallback != g_state.english_fallback.end() && !fallback->second.empty())
        {
            return fallback->second;
        }

        return key;
    }
}
