#include "Resolvers/BaseObjectResolver.h"

#include "Offsets.h"
#include "RuntimeOffsets.h"
#include "Memory/MemoryValidation.h"

#include <spdlog/spdlog.h>

bool BaseObjectResolver::TryResolve(VmmAccessor&     mem,
                                    uint32_t         pid,
                                    uint64_t         gameBase,
                                    RuntimeSession&  session,
                                    NetworkMetadata& metadata,
                                    LocalPlayerData& player)
{
    // -----------------------------------------------------------------
    // 1. Validate the game base address
    // -----------------------------------------------------------------
    if (!MemoryValidation::IsValidUserAddress(gameBase)) {
        m_lastFailureReason = "gameBase is not a valid user address";
        return false;
    }

    // -----------------------------------------------------------------
    // 2. World pointer  (indirect read)
    // -----------------------------------------------------------------
    uint64_t worldAddress = 0;
    if (!mem.TryReadPointer(pid, gameBase + RuntimeOffsets::Base::World, worldAddress)) {
        m_lastFailureReason = "Failed to read World pointer from gameBase";
        return false;
    }

    // -----------------------------------------------------------------
    // 3. NetworkManager address  (direct offset, NOT a pointer read)
    // -----------------------------------------------------------------
    uint64_t networkManagerAddress = gameBase + RuntimeOffsets::Base::NetworkManager;

    if (!MemoryValidation::IsValidUserAddress(networkManagerAddress)) {
        m_lastFailureReason = "Computed networkManagerAddress is not a valid user address";
        return false;
    }

    // -----------------------------------------------------------------
    // 4. Build session
    // -----------------------------------------------------------------
    session = RuntimeSession{
        .processId             = pid,
        .gameBaseAddress       = gameBase,
        .worldAddress          = worldAddress,
        .networkManagerAddress = networkManagerAddress,
    };

    // -----------------------------------------------------------------
    // 5. Network metadata (non-fatal — null NetworkClient is normal while
    //    the game is loading or in the main menu; the live loop retries it)
    // -----------------------------------------------------------------
    if (!m_networkMetadataReader.TryRead(mem, session, metadata)) {
        spdlog::debug("BaseObjectResolver: NetworkMetadata unavailable ({}); proceeding.",
                      m_networkMetadataReader.LastFailureReason());
    }

    // -----------------------------------------------------------------
    // 6. Local player — required to confirm the player is in-game
    // -----------------------------------------------------------------
    if (!m_localPlayerResolver.TryResolve(mem, session, player)) {
        m_lastFailureReason = "LocalPlayerResolver failed: "
                              + m_localPlayerResolver.LastFailureReason();
        return false;
    }

    m_lastFailureReason.clear();
    return true;
}

bool BaseObjectResolver::IsStillValid(VmmAccessor& mem, const RuntimeSession& session)
{
    const uint32_t pid = session.processId;

    // Re-read world pointer and compare
    uint64_t currentWorld = 0;
    if (!mem.TryReadPointer(pid,
                            session.gameBaseAddress + RuntimeOffsets::Base::World,
                            currentWorld))
    {
        return false;
    }

    if (currentWorld != session.worldAddress) {
        return false;
    }

    // Re-compute network manager and compare
    uint64_t currentNetworkManager = session.gameBaseAddress + RuntimeOffsets::Base::NetworkManager;
    if (currentNetworkManager != session.networkManagerAddress) {
        return false;
    }

    return true;
}
