-- ROBLOX MOVED: expect/utils.lua

local function subsetEquality(object: any, subset: any): boolean | nil
	-- subsetEquality needs to keep track of the references
	-- it has already visited to avoid infinite loops in case
	-- there are circular references in the subset passed to it
	local function subsetEqualityWithContext(seenReferences_: { [any]: boolean }?)
		local seenReferences = seenReferences_ or {}

		return function(object_: any, subset_: any): boolean | nil
			if not isObjectWithKeys(subset_) then
				return nil
			end

			return Array.every(Object.keys(subset_), function(key)
				if isObjectWithKeys(subset_[key]) then
					if seenReferences[subset_[key]] then
						--[[
								ROBLOX TODO: (ADO-1217) replace the line below
								once Map/Set functionality is implemented

								return equals(localObject[key], localSubset[key], {iterableEquality})
							]]
						return equals(object_[key], subset_[key], { iterableEquality })
					end
					seenReferences[subset_[key]] = true
				end
				local result = object_ ~= nil
					and hasPropertyInObject(object_, key)
					and equals(object_[key], subset_[key], {
						--[[
									ROBLOX TODO: (ADO-1217) uncomment the line
									iterableEquality,
								]]
						subsetEqualityWithContext(seenReferences),
					})
				-- The main goal of using seenReference is to avoid
				-- circular node on tree.
				-- It will only happen within a parent and its child, not a
				-- node and nodes next to it (same level)
				-- We should keep the reference for a parent and its child
				-- only
				-- Thus we should delete the reference immediately so that
				-- it doesn't interfere other nodes within the same level
				-- on tree.
				seenReferences[subset_[key]] = nil
				return result
			end)
		end
	end

	return subsetEqualityWithContext()(object, subset)
end
