-- ROBLOX deviation: saves a valid Roblox ModuleScript

local function saveSnapshotFile(snapshotData: SnapshotData, snapshotPath: ConfigPath)
	local snapshots = {
		writeSnapshotVersion(),
		"local exports = {}",
	}

	for _, key in ipairs(alphanumsort(Object.keys(snapshotData))) do
		table.insert(
			snapshots,
			"exports[ [=["
				-- ROBLOX deviation: we don't call printBacktickString here since we inline the
				-- multiline literal
				.. escapeBacktickString(key)
				.. "]=] ] = "
				.. printBacktickString(normalizeNewLines(snapshotData[key]))
		)
		-- ROBLOX deviation: we don't append a semicolon
	end
	table.insert(snapshots, "return exports")

	if FileSystemService == nil then
		FileSystemService = getFileSystemService() or false
	end
	-- ROBLOX deviation: error when FileSystemService doesn't exist
	if not FileSystemService then
		error(Error("Attempting to save snapshots in an environment where FileSystemService is inaccessible."))
	end
	ensureDirectoryExists(snapshotPath)
	FileSystemService:WriteFile(snapshotPath, table.concat(snapshots, "\n\n"))
end

local function deepMergeArray(target: Array<any>, source: Array<any>)
	local mergedOutput = Array.from(target)

	for index, sourceElement in ipairs(source) do
		local targetElement = mergedOutput[index]

		if Array.isArray(target[index]) then
			mergedOutput[index] = deepMergeArray(target[index], sourceElement)
		elseif isObject(targetElement) then
			mergedOutput[index] = deepMerge(target[index], sourceElement)
		else
			-- Source does not exist in target or target is primitive and cannot be deep merged
			mergedOutput[index] = sourceElement
		end
	end

	return mergedOutput
end

function deepMerge(target: any, source: any): any
	if isObject(target) and isObject(source) then
		local mergedOutput = {}
		for key, value in pairs(target) do
			mergedOutput[key] = value
		end

		for key, value in pairs(source) do
			if isObject(source[key]) and not source[key]["$$typeof"] then
				if not target[key] then
					Object.assign(mergedOutput, { [key] = source[key] })
				else
					mergedOutput[key] = deepMerge(target[key], source[key])
				end
			elseif Array.isArray(source[key]) then
				mergedOutput[key] = deepMergeArray(target[key], source[key])
			else
				Object.assign(mergedOutput, { [key] = source[key] })
			end
		end

		return mergedOutput
	elseif Array.isArray(target) and Array.isArray(source) then
		return deepMergeArray(target, source)
	end

	return target
end
