#pragma once

#include <cstddef>
#include <string>
#include <string_view>
#include <utility>

#include "foundation/text/TextTransliteration.h"
#include "adapters/il2cpp/Il2CppObjectInstance.h"
#include "adapters/il2cpp/Il2CppObjectAccess.h"
#include "adapters/il2cpp/Il2CppString.h"
#include "adapters/unity/bindings/UnityTypes.h"
#include "adapters/il2cpp/Il2CppCollections.h"
#include "adapters/unity/bindings/UnityApi.h"
#include "adapters/eft/common/ManagedObjectAccess.h"

namespace ta::adapters::eft::common
{
    inline bool is_usable_object(const Il2CppObject* object)
    {
        return object && object->klass;
    }

    inline Il2CppObject* get_profile(Il2CppObject* player)
    {
        if (!is_usable_object(player)) return nullptr;

        const auto result = ta::adapters::il2cpp::ManagedRuntime::invoke<Il2CppObject*>(
            player, "get_Profile", ta::adapters::il2cpp::LookupPolicy::IncludeBaseClasses);
        return result ? result.value() : nullptr;
    }

    inline std::string get_profile_nickname(Il2CppObject* profile)
    {
        if (!is_usable_object(profile)) return {};

        const auto result = ta::adapters::il2cpp::ManagedRuntime::invoke<Il2CppString*>(
            profile, "get_Nickname", ta::adapters::il2cpp::LookupPolicy::IncludeBaseClasses);
        Il2CppString* nickname = result ? result.value() : nullptr;
        if (!nickname) return {};

        return ta::foundation::text::to_english(ta::adapters::il2cpp::conv_wstring(nickname));
    }

    inline std::string get_player_nickname(Il2CppObject* player)
    {
        return get_profile_nickname(get_profile(player));
    }

    inline std::string get_player_nickname(ta::adapters::il2cpp::Il2CppObjectInstance player)
    {
        return get_player_nickname(player.get_instance());
    }

    struct WorldPlayerEntry
    {
        Il2CppObject* object = nullptr;
        ta::adapters::il2cpp::Il2CppObjectInstance instance;
        Il2CppObject* profile = nullptr;
        std::string nickname;
        bool is_local = false;

        WorldPlayerEntry(Il2CppObject* player_object,
                         Il2CppObject* player_profile,
                         std::string player_nickname,
                         const bool local)
            : object(player_object),
              instance(player_object),
              profile(player_profile),
              nickname(std::move(player_nickname)),
              is_local(local)
        {
        }
    };

    inline ta::adapters::unity::list* alive_players_list(ta::adapters::il2cpp::Il2CppObjectInstance game_world_instance)
    {
        if (!game_world_instance.get_instance())
            return nullptr;

        ta::adapters::unity::list* players = nullptr;
        return game_world_instance.try_get_field("AllAlivePlayersList", players) ? players : nullptr;
    }

    template <typename Callback>
    void for_each_alive_player(ta::adapters::il2cpp::Il2CppObjectInstance game_world_instance,
                               std::string_view local_nickname,
                               Callback&& callback,
                               const std::size_t max_players = 0)
    {
        ta::adapters::unity::list* alive_players = alive_players_list(game_world_instance);
        if (!alive_players || !alive_players->m_p_list_array)
            return;

        std::size_t visited = 0;
        for_each_list_object(alive_players, [&](Il2CppObject* player_object, int index)
        {
            if (max_players > 0 && visited >= max_players)
                return;
            if (!is_usable_object(player_object))
                return;

            ++visited;

            Il2CppObject* profile = get_profile(player_object);
            std::string nickname = get_profile_nickname(profile);
            const bool is_local = !local_nickname.empty() && std::string_view(nickname) == local_nickname;
            WorldPlayerEntry entry{
                player_object,
                profile,
                std::move(nickname),
                is_local
            };

            callback(entry, index);
        });
    }
}
