#include "adapters/eft/world/DoorUnlockService.h"

#include <algorithm>
#include <cstddef>
#include <string>

#include "adapters/il2cpp/Il2CppObjectInstance.h"
#include "adapters/il2cpp/Il2CppResolver.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"

namespace
{
    enum class DoorState : int
    {
        OpenOrUnlocked = 2
    };

    using set_door_state_sig = void(*)(Il2CppObject*, int);

    [[nodiscard]] Il2CppObject* resolve_door_system_type(const Il2CppClass* game_world_class)
    {
        if (!game_world_class || !game_world_class->image) return nullptr;

        return ta::adapters::il2cpp::get_system_type(
            game_world_class->image,
            "EFT.Interactive",
            "Door");
    }

    [[nodiscard]] set_door_state_sig resolve_set_door_state(Il2CppObject* door)
    {
        if (!door || !door->klass) return nullptr;

        ta::adapters::il2cpp::Il2CppObjectInstance door_instance(door);
        return door_instance.get_method<set_door_state_sig>("set_DoorState", 1);
    }
}

ta::adapters::eft::world::WorldActionResult ta::adapters::eft::world::unlock_doors(const Il2CppClass* game_world_class)
{
    WorldActionResult result{};

    if (!ta::adapters::unity::is_initialized())
    {
        result.message = "Unlock Doors: Unity API not initialized";
        return result;
    }

    Il2CppObject* door_system_type = resolve_door_system_type(game_world_class);
    if (!door_system_type)
    {
        result.message = "Unlock Doors: Door type not found";
        return result;
    }

    size_t found_doors = 0;
    Il2CppObject** doors = ta::adapters::unity::find_objects_of_type(door_system_type, &found_doors);

    if (!doors || found_doors == 0)
    {
        result.message = "Unlock Doors: no doors found";
        return result;
    }

    constexpr size_t max_safe_doors = 512;
    const size_t limit = (std::min)(found_doors, max_safe_doors);

    for (size_t i = 0; i < limit; ++i)
    {
        Il2CppObject* door = doors[i];
        if (!door || !door->klass)
        {
            ++result.skipped;
            continue;
        }

        set_door_state_sig set_door_state = resolve_set_door_state(door);
        if (!set_door_state)
        {
            ++result.skipped;
            continue;
        }

        set_door_state(door, static_cast<int>(DoorState::OpenOrUnlocked));
        ++result.changed;
    }

    if (found_doors > max_safe_doors)
        result.skipped += static_cast<int>(found_doors - max_safe_doors);

    result.executed = true;
    result.message =
        "Unlock Doors: changed " + std::to_string(result.changed) +
        ", skipped " + std::to_string(result.skipped) +
        ", found " + std::to_string(found_doors);
    return result;
}
