#include "l10n.hpp"

#include <components/debug/debuglog.hpp>
#include <components/l10n/manager.hpp>
#include <components/lua/luastate.hpp>

namespace
{
    struct L10nContext
    {
        std::shared_ptr<const l10n::MessageBundles> mData;
    };

    void getICUArgs(std::string_view messageId, const sol::table& table, std::vector<icu::UnicodeString>& argNames,
        std::vector<icu::Formattable>& args)
    {
        for (auto& [key, value] : table)
        {
            // Argument values
            if (value.is<std::string>())
                args.push_back(icu::Formattable(LuaUtil::cast<std::string>(value).c_str()));
            // Note: While we pass all numbers as doubles, they still seem to be handled appropriately.
            // Numbers can be forced to be integers using the argType number and argStyle integer
            //     E.g. {var, number, integer}
            else if (value.is<double>())
                args.push_back(icu::Formattable(LuaUtil::cast<double>(value)));
            else
            {
                Log(Debug::Error) << "Unrecognized argument type for key \"" << LuaUtil::cast<std::string>(key)
                                  << "\" when formatting message \"" << messageId << "\"";
            }

            // Argument names
            const auto str = LuaUtil::cast<std::string>(key);
            argNames.push_back(icu::UnicodeString::fromUTF8(icu::StringPiece(str.data(), str.size())));
        }
    }
}

namespace sol
{
    template <>
    struct is_automagical<L10nContext> : std::false_type
    {
    };
}

namespace LuaUtil
{
    sol::function initL10nLoader(lua_State* L, l10n::Manager* manager)
    {
        sol::state_view lua(L);
        sol::usertype<L10nContext> ctxDef = lua.new_usertype<L10nContext>("L10nContext");
        ctxDef[sol::meta_function::call]
            = [](const L10nContext& ctx, std::string_view key, sol::optional<sol::table> args) {
                  std::vector<icu::Formattable> argValues;
                  std::vector<icu::UnicodeString> argNames;
                  if (args)
                      getICUArgs(key, *args, argNames, argValues);
                  return ctx.mData->formatMessage(key, argNames, argValues);
              };

        return sol::make_object(
            lua, [manager](const std::string& contextName, sol::optional<std::string> fallbackLocale) {
                if (fallbackLocale)
                    return L10nContext{ manager->getContext(contextName, *fallbackLocale) };
                else
                    return L10nContext{ manager->getContext(contextName) };
            });
    }
}