#pragma once

#include <optional>
#include <string>
#include <vector>

#include "adapters/eft/common/PlayerWorldReader.h"
#include "adapters/eft/world/WorldAccess.h"
#include "foundation/diagnostics/OnceLogger.h"

namespace ta::adapters::eft::esp
{
class EspPlayerScanner
{
public:
    static std::vector<ta::features::esp::EspPlayer> scan_players(
        ta::adapters::il2cpp::Il2CppObjectInstance game_world_instance,
        const std::string& main_nickname)
    {
        std::vector<ta::features::esp::EspPlayer> result;

        ta::adapters::eft::common::for_each_alive_player(
            game_world_instance,
            main_nickname,
            [&](const ta::adapters::eft::common::WorldPlayerEntry& player, int)
            {
                if (player.is_local)
                    return;

                if (player.nickname.empty())
                {
                    ta::foundation::diagnostics::OnceLogger::error(
                        "ESP", "player-nickname-unavailable",
                        "Player ESP skipped: profile nickname unavailable");
                    return;
                }

                ta::adapters::unity::vector3 position{};
                if (!ta::adapters::eft::common::get_component_position(player.object, position))
                    return;

                const auto is_pmc = is_pmc_by_dogtag(player.instance);
                if (!is_pmc)
                    return;

                result.push_back({player.nickname, *is_pmc, position});
            });
        return result;
    }

private:
    static std::optional<bool> is_pmc_by_dogtag(
        ta::adapters::il2cpp::Il2CppObjectInstance player_instance)
    {
        Il2CppObject* inventory_controller_ptr = nullptr;
        if (!player_instance.try_get_field("_inventoryController", inventory_controller_ptr) ||
            !inventory_controller_ptr)
        {
            ta::foundation::diagnostics::OnceLogger::error(
                "ESP", "player-inventory-controller-unavailable",
                "Player classification failed: _inventoryController unavailable");
            return std::nullopt;
        }

        ta::adapters::il2cpp::Il2CppObjectInstance inventory_controller(inventory_controller_ptr);
        const auto get_inventory =
            inventory_controller.get_method<Il2CppObject*(*)(Il2CppObject*)>("get_Inventory", 0);
        if (!get_inventory)
        {
            ta::foundation::diagnostics::OnceLogger::error(
                "ESP", "player-inventory-method-unavailable",
                "Player classification failed: get_Inventory unavailable");
            return std::nullopt;
        }

        Il2CppObject* inventory_ptr = get_inventory(inventory_controller.get_instance());
        if (!inventory_ptr)
        {
            ta::foundation::diagnostics::OnceLogger::error(
                "ESP", "player-inventory-null",
                "Player classification failed: Inventory is null");
            return std::nullopt;
        }

        ta::adapters::il2cpp::Il2CppObjectInstance inventory(inventory_ptr);
        Il2CppObject* equipment_ptr = nullptr;
        if (!inventory.try_get_field("Equipment", equipment_ptr) || !equipment_ptr)
        {
            ta::foundation::diagnostics::OnceLogger::error(
                "ESP", "player-equipment-unavailable",
                "Player classification failed: Inventory.Equipment unavailable");
            return std::nullopt;
        }

        ta::adapters::il2cpp::Il2CppObjectInstance equipment(equipment_ptr);
        const auto get_slot =
            equipment.get_method<Il2CppObject*(*)(Il2CppObject*, int)>("GetSlot", 1);
        if (!get_slot)
        {
            ta::foundation::diagnostics::OnceLogger::error(
                "ESP", "player-dogtag-slot-method-unavailable",
                "Player classification failed: Equipment.GetSlot unavailable");
            return std::nullopt;
        }

        Il2CppObject* equipment_slot_ptr = get_slot(equipment.get_instance(), 13);
        if (!equipment_slot_ptr)
        {
            ta::foundation::diagnostics::OnceLogger::error(
                "ESP", "player-dogtag-slot-null",
                "Player classification failed: dogtag slot is null");
            return std::nullopt;
        }

        ta::adapters::il2cpp::Il2CppObjectInstance equipment_slot(equipment_slot_ptr);
        const auto get_contained_item =
            equipment_slot.get_method<Il2CppObject*(*)(Il2CppObject*)>(
                "get_ContainedItem", 0);
        if (!get_contained_item)
        {
            ta::foundation::diagnostics::OnceLogger::error(
                "ESP", "player-dogtag-item-method-unavailable",
                "Player classification failed: get_ContainedItem unavailable");
            return std::nullopt;
        }

        return get_contained_item(equipment_slot.get_instance()) != nullptr;
    }
};
}
