local OFHotBrassPatch = {
    patched = false,
}

local function isPatchToggleEnabled()
    local vars = SandboxVars and SandboxVars.OpinionatedFirearms
    if vars and vars.HBVCEFAmmoMakerPatch ~= nil then
        return vars.HBVCEFAmmoMakerPatch == true
    end

    return true
end

local function isSessionEligible()
    return isPatchToggleEnabled()
end

local function getAmmoMakerFiredCasing(ammoType)
    if type(ammoType) ~= "string" or ammoType == "" then
        return nil
    end

    local function partDataToFiredType(partData)
        if type(partData) ~= "table" then
            return nil
        end

        local firedType = partData.partFired or partData.partOld
        if type(firedType) ~= "string" or firedType == "" then
            return nil
        end

        return firedType
    end

    if type(ammoMakerAmmoParts) ~= "table" then
        return nil
    end

    if type(ammoMakerGetCasingType) == "function" then
        local ok, casingType = pcall(ammoMakerGetCasingType, ammoType)
        if ok and type(casingType) == "string" then
            local firedType = partDataToFiredType(ammoMakerAmmoParts[casingType])
            if firedType then
                return firedType
            end
        end
    end

    -- Fallback path that doesn't depend on Ammo Maker active-mod resolution.
    if type(ammoMakerAmmoTypes) == "table" and type(ammoMakerAmmoData) == "table" then
        local ammoDef = ammoMakerAmmoTypes[ammoType]
        if type(ammoDef) == "table" and type(ammoDef.ammoTypes) == "table" then
            for i = 1, #ammoDef.ammoTypes do
                local ammoKey = ammoDef.ammoTypes[i]
                local ammoData = ammoMakerAmmoData[ammoKey]
                if type(ammoData) == "table" and type(ammoData.casingType) == "string" then
                    local firedType = partDataToFiredType(ammoMakerAmmoParts[ammoData.casingType])
                    if firedType then
                        return firedType
                    end
                end
            end
        end
    end

    return nil
end

local function applyPatch()
    if OFHotBrassPatch.patched then
        return true
    end

    if not isSessionEligible() then
        return false
    end

    if type(SpentCasingPhysics) ~= "table" then
        return false
    end

    if type(SpentCasingPhysics.getItemToEject) ~= "function" then
        return false
    end

    if type(SpentCasingPhysics.doSpawnCasing) ~= "function" then
        return false
    end

    local originalGetItemToEject = SpentCasingPhysics.getItemToEject
    local originalDoSpawnCasing = SpentCasingPhysics.doSpawnCasing

    SpentCasingPhysics.getItemToEject = function(ammoType)
        local mappedType = getAmmoMakerFiredCasing(ammoType)
        if mappedType then
            return mappedType
        end

        return originalGetItemToEject(ammoType)
    end

    SpentCasingPhysics.doSpawnCasing = function(player, weapon, params, racking, optionalItem)
        if not optionalItem and weapon and weapon.getAmmoType then
            local ammoType = weapon:getAmmoType()
            if ammoType then
                local mappedType = getAmmoMakerFiredCasing(tostring(ammoType))
                if mappedType then
                    optionalItem = mappedType
                    if racking then
                        -- Force rack ejects to stay as empties for this patch.
                        racking = false
                    end
                end
            end
        end

        return originalDoSpawnCasing(player, weapon, params, racking, optionalItem)
    end

    OFHotBrassPatch.patched = true
    return true
end

local function tryPatchOnTick()
    if OFHotBrassPatch.patched then
        Events.OnTick.Remove(tryPatchOnTick)
        return
    end

    applyPatch()
end

Events.OnTick.Add(tryPatchOnTick)
