-- Misc trainer features ported from pirate-perfection:
--   * Name Spoof          (hide/replace the Steam name other players see)
--   * Interact For Team    (make EVERY interaction in the lobby instant)
--   * Item Stealing        (copy teammates' masks & weapon mods into YOUR inventory)
--
-- All wired onto HexTrainer's CloneClass/.orig + setting/JSON conventions.

HT.Misc = {}

-- =============================================================================
-- Name Spoof
-- =============================================================================
-- A peer's displayed name is their local copy of NetworkPeer:name(). We return
-- the spoof for OUR local peer, and also push it through set_name so it rides
-- along in the info the game syncs to others. Most reliable when set BEFORE you
-- join a lobby (the name is exchanged during the connection handshake); changing
-- it mid-lobby updates your own UI/chat immediately and propagates on the next
-- outfit/info sync.
HT.Misc._nameHooked = false

function HT.Misc:installNameHook()
    if HT.Misc._nameHooked then return end
    if not _G.NetworkPeer then return end
    _G.CloneClass(NetworkPeer)
    function NetworkPeer:name()
        local spoof = Global.hex_spoofed_name
        if spoof and spoof ~= "" then
            local session = managers.network and managers.network:session()
            if session and self == session:local_peer() then
                return spoof
            end
        end
        return NetworkPeer.orig.name(self)
    end
    function NetworkPeer:set_name(name)
        local session = managers.network and managers.network:session()
        if session and self == session:local_peer()
        and Global.hex_spoofed_name and Global.hex_spoofed_name ~= "" then
            name = Global.hex_spoofed_name
        end
        return NetworkPeer.orig.set_name(self, name)
    end
    HT.Misc._nameHooked = true
end

function HT.Misc:setNameSpoof(name)
    if name then name = tostring(name) end
    pcall(function()
        local session = managers.network and managers.network:session()
        local peer = session and session:local_peer()
        if peer and name and name ~= "" and not HT.Misc._originalLocalName then
            HT.Misc._originalLocalName = NetworkPeer.orig and NetworkPeer.orig.name(peer) or peer:name()
        end
    end)
    Global.hex_spoofed_name = (name and name ~= "") and name or nil
    HT:setSetting("name_spoof", Global.hex_spoofed_name or "")
    HT.Misc:installNameHook()
    -- Push it onto the live local peer so chat/scoreboard pick it up now.
    pcall(function()
        local session = managers.network and managers.network:session()
        if session and session:local_peer() then
            local live_name = Global.hex_spoofed_name or HT.Misc._originalLocalName
            if live_name then session:local_peer():set_name(live_name) end
        end
    end)
    if Global.hex_spoofed_name then
        HT:addAlert("Name spoofed to: " .. Global.hex_spoofed_name .. " (best set before joining)", HT.colors.success, false)
    else
        HT:addAlert("Name spoof cleared", HT.colors.info, false)
    end
end

-- Re-apply on boot/heist so the hook survives class reloads.
function HT.Misc:reapplyNameSpoof()
    local saved = HT:getSetting("name_spoof")
    if saved and saved ~= "" then
        Global.hex_spoofed_name = saved
        HT.Misc:installNameHook()
        pcall(function()
            local session = managers.network and managers.network:session()
            if session and session:local_peer() then
                session:local_peer():set_name(saved)
            end
        end)
    end
end

-- =============================================================================
-- Interact For Teammates  (lobby-wide instant interactions)
-- =============================================================================
-- The host validates how long an interaction took, so zeroing every interaction
-- timer in tweak_data makes interactions instant for the WHOLE team (drills,
-- ziplines, ammo bags, answering pagers, reviving, etc.). We snapshot the
-- originals so the toggle is reversible.
HT.Misc._interactBackup = nil

function HT.Misc:setInteractForTeam(value)
    HT:setSetting("enable_interact_for_team", value)
    pcall(function()
        local interactions = tweak_data and tweak_data.interaction
        if not interactions then return end
        if value then
            HT.Misc._interactBackup = HT.Misc._interactBackup or {}
            for id, data in pairs(interactions) do
                if type(data) == "table" and data.timer then
                    if HT.Misc._interactBackup[id] == nil then
                        HT.Misc._interactBackup[id] = data.timer
                    end
                    data.timer = 0
                end
            end
        elseif HT.Misc._interactBackup then
            for id, timer in pairs(HT.Misc._interactBackup) do
                if interactions[id] then interactions[id].timer = timer end
            end
        end
    end)
    HT:addAlert(value and "Team instant interaction ON" or "Team instant interaction OFF", HT.colors.success, false)
end

function HT.Misc:reapplyInteractForTeam()
    if HT:getSetting("enable_interact_for_team") then
        HT.Misc:setInteractForTeam(true)
    end
end

-- Crash guard: PlayerManager:set_carry does `tweak_data.carry[carry_id].type`
-- with no nil-check, so picking up a bag whose carry_id isn't fully synced yet
-- hard-crashes (playermanager.lua:5395 "attempt to index local 'carry_data'").
-- Instant interaction / Interact-For-Team make bag pickups complete instantly,
-- which can race that sync. We wrap set_carry to bail safely on a bad id. This
-- is always-on safety (harmless when ids are valid) installed at heist start.
HT.Misc._carrySafetyHooked = false
function HT.Misc:installCarrySafety()
    if HT.Misc._carrySafetyHooked then return end
    if not _G.PlayerManager then return end
    _G.CloneClass(PlayerManager)
    function PlayerManager:set_carry(carry_id, ...)
        if not (carry_id and tweak_data.carry and tweak_data.carry[carry_id]) then
            return
        end
        return PlayerManager.orig.set_carry(self, carry_id, ...)
    end
    HT.Misc._carrySafetyHooked = true
end

-- =============================================================================
-- Item Stealing  ("I Want That" -- copy teammate loadouts into your inventory)
-- =============================================================================
-- The PP version is a giant custom-UI browser. We keep the useful core: pull
-- every teammate's mask (+ material/pattern/color) and every non-default weapon
-- mod straight into your blackmarket inventory, so you own them to craft later.

local function addItem(category, id)
    if not id or id == "" then return end
    if not (tweak_data.blackmarket[category] and tweak_data.blackmarket[category][id]) then return end
    local gv = managers.blackmarket:get_global_value(category, id) or "normal"
    pcall(function() managers.blackmarket:add_to_inventory(gv, category, id, false) end)
end

local function stealOutfit(outfit, added)
    if not outfit then return end
    -- Mask + its components
    if outfit.mask then
        addItem("masks", outfit.mask.mask_id); added.n = added.n + 1
        local bp = outfit.mask.blueprint
        if bp then
            if bp.material then addItem("materials", bp.material.id) end
            if bp.pattern  then addItem("textures",  bp.pattern.id) end
            if bp.color    then addItem("colors",    bp.color.id) end
        end
    end
    -- Weapon mods on both weapons
    for _, slot in ipairs({ outfit.primary, outfit.secondary }) do
        if slot and slot.blueprint then
            local default = {}
            pcall(function()
                for _, p in pairs(managers.weapon_factory:get_default_blueprint_by_factory_id(slot.factory_id) or {}) do
                    default[p] = true
                end
            end)
            for _, mod in pairs(slot.blueprint) do
                if not default[mod] then addItem("weapon_mods", mod); added.n = added.n + 1 end
            end
        end
    end
end

-- Copy every teammate's loadout (and optionally your own) into your inventory.
function HT.Misc:stealAllLoadouts()
    local session = managers.network and managers.network:session()
    if not session then
        HT:addAlert("Not in a lobby", HT.colors.warning, false)
        return
    end
    local added = { n = 0 }
    for _, peer in pairs(session:peers()) do
        pcall(function() stealOutfit(peer:blackmarket_outfit(), added) end)
    end
    pcall(function() if managers.menu_component then managers.menu_component:refresh_player_profile_gui() end end)
    HT:addAlert("Copied ~" .. added.n .. " items from teammates into your inventory", HT.colors.success, false)
end
