-- Import into an entire datastore type:
local function importDataStoresFromTable(origin, destination, warnFunc, methodName, prefix, isOrdered)
	for name, scopes in pairs(origin) do
		if type(name) ~= "string" then
			warnFunc(("%s: ignored %s > %q (name is not a string, but a %s)")
				:format(methodName, prefix, tostring(name), typeof(name)))
		elseif type(scopes) ~= "table" then
			warnFunc(("%s: ignored %s > %q (scope list is not a table, but a %s)")
				:format(methodName, prefix, name, typeof(scopes)))
		elseif #name == 0 then
			warnFunc(("%s: ignored %s > %q (name is an empty string)")
				:format(methodName, prefix, name))
		elseif #name > Constants.MAX_LENGTH_NAME then
			warnFunc(("%s: ignored %s > %q (name exceeds %d character limit)")
				:format(methodName, prefix, name, Constants.MAX_LENGTH_NAME))
		else
			for scope, data in pairs(scopes) do
				if type(scope) ~= "string" then
					warnFunc(("%s: ignored %s > %q > %q (scope is not a string, but a %s)")
						:format(methodName, prefix, name, tostring(scope), typeof(scope)))
				elseif type(data) ~= "table" then
					warnFunc(("%s: ignored %s > %q > %q (data list is not a table, but a %s)")
						:format(methodName, prefix, name, scope, typeof(data)))
				elseif #scope == 0 then
					warnFunc(("%s: ignored %s > %q > %q (scope is an empty string)")
						:format(methodName, prefix, name, scope))
				elseif #scope > Constants.MAX_LENGTH_SCOPE then
					warnFunc(("%s: ignored %s > %q > %q (scope exceeds %d character limit)")
						:format(methodName, prefix, name, scope, Constants.MAX_LENGTH_SCOPE))
				else
					if not destination[name] then
						destination[name] = {}
					end
					if not destination[name][scope] then
						destination[name][scope] = {}
					end
					Utils.importPairsFromTable(
						data,
						destination[name][scope],
						Interfaces[destination[name][scope]],
						warnFunc,
						methodName,
						("%s > %q > %q"):format(prefix, name, scope),
						isOrdered
					)
				end
			end
		end
	end
end

function MockDataStoreManager.ImportFromJSON(content, verbose)
	assert(type(content) == "table")
	assert(verbose == nil or type(verbose) == "boolean")

	local warnFunc = warn -- assume verbose as default
	if verbose == false then -- intentional formatting
		warnFunc = function() end
	end

	if type(content.GlobalDataStore) == "table" then
		Utils.importPairsFromTable(
			content.GlobalDataStore,
			Data.GlobalDataStore,
			Interfaces[Data.GlobalDataStore],
			warnFunc,
			"ImportFromJSON",
			"GlobalDataStore",
			false
		)
	end
	if type(content.DataStore) == "table" then
		importDataStoresFromTable(
			content.DataStore,
			Data.DataStore,
			warnFunc,
			"ImportFromJSON",
			"DataStore",
			false
		)
	end
	if type(content.OrderedDataStore) == "table" then
		importDataStoresFromTable(
			content.OrderedDataStore,
			Data.OrderedDataStore,
			warnFunc,
			"ImportFromJSON",
			"OrderedDataStore",
			true
		)
	end
end

local function clearTable(t)
	for i,_ in pairs(t) do
		t[i] = nil
	end
end

function MockDataStoreManager.ResetData()
	for _, interface in pairs(Interfaces) do
		for key, _ in pairs(interface.__data) do
			interface.__data[key] = nil
			interface.__event:Fire(key, nil)
		end
		interface.__getCache = {}
		interface.__writeCache = {}
		interface.__writeLock = {}
		if interface.__sorted then
			interface.__sorted = {};
            interface.__ref = {};
            interface.__changed = false;
		end
	end

	clearTable(Data.GlobalDataStore)

	for _, scopes in pairs(Data.DataStore) do
		for _, data in pairs(scopes) do
			clearTable(data)
		end
	end

	for _, scopes in pairs(Data.OrderedDataStore) do
		for _, data in pairs(scopes) do
			clearTable(data)
		end
	end
end

return MockDataStoreManager