--[=[
	Similiar to Fusion's ComputedPairs, where the changes are cached, and the lifetime limited.
	@param source Observable<T> | any
	@param compute (key: any, value: any, innerMaid: Maid) -> Instance | Observable<Instance>
	@return Observable<Brio<Instance>>
]=]

function Blend.ComputedPairs(source, compute)
	local sourceObservable = Blend.toPropertyObservable(source) or Rx.of(source)

	return Observable.new(function(sub)
		local cache = {}
		local topMaid = Maid.new()

		local maidForKeys = Maid.new()
		topMaid:GiveTask(maidForKeys)

		topMaid:GiveTask(sourceObservable:Subscribe(function(newValue)
			-- It's gotta be a table
			assert(type(newValue) == "table", "Bad value emitted from source")

			local excluded = {}
			for key, _ in pairs(cache) do
				excluded[key] = true
			end

			for key, value in pairs(newValue) do
				excluded[key] = nil

				if cache[key] ~= value then
					local innerMaid = Maid.new()
					local result = compute(key, value, innerMaid)

					local brio = Brio.new(result)
					innerMaid:GiveTask(brio)

					sub:Fire(brio)

					maidForKeys[key] = innerMaid
					cache[key] = value
				end
			end

			for key, _ in pairs(excluded) do
				maidForKeys[key] = nil
				cache[key] = nil
			end
		end), sub:GetFailComplete())

		return topMaid
	end)
end
