-- given an Instance and a property-value table subset, returns
-- an InstanceSubset object representing the subset of Instance with values in the subset table
-- and a InstanceSubset object representing the subset table

local function getInstanceSubset(instance: any, subset: any, seenReferences): any
	seenReferences = seenReferences or {}

	local trimmed: any = {}
	seenReferences[instance] = trimmed

	-- return non-table primitives
	if equals(instance, subset) then
		return subset, subset
	elseif typeof(subset) ~= "table" or isAsymmetric(subset) then
		return instance, subset
	end

	-- collect non-table primitive values
	local newSubset = {}
	for k, v in pairs(subset) do
		if typeof(v) ~= "table" then
			newSubset[k] = v
		end
	end

	local propsAndChildren = getRobloxProperties(instance.ClassName)
	for _, v in ipairs(instance:getChildren()) do
		table.insert(propsAndChildren, v.Name)
	end

	for i, prop in
		ipairs(Array.filter(propsAndChildren, function(prop)
			return hasPropertyInObject(subset, prop)
		end))
	do
		if seenReferences[instance[prop]] ~= nil then
			error("Circular reference passed into .toMatchInstance(subset)")
		else
			local nestedSubset
			trimmed[prop], nestedSubset = getInstanceSubset(instance[prop], subset[prop], seenReferences)
			newSubset[prop] = nestedSubset
		end
	end

	-- serialize with a different ClassName if the subset has ClassName defined,
	-- otherwise treat it as the same class as the original Instance
	local subsetClassName = instance.ClassName
	if typeof(subset) == "table" and rawget(subset, "ClassName") then
		subsetClassName = rawget(subset, "ClassName")
	end

	return InstanceSubset.new(instance.ClassName, trimmed), InstanceSubset.new(subsetClassName, newSubset)
end

return {
	getRobloxProperties = getRobloxProperties,
	instanceSubsetEquality = instanceSubsetEquality,
	InstanceSubset = InstanceSubset,
	getInstanceSubset = getInstanceSubset,
}
