local function s(s,U,p)return string.format("\091\083\116\111\114\101\058\"\037\115\"\059\037\115\075\101\121\058\"\037\115\"\093",s,U~=nil and string.format("\083\099\111\112\101\058\"\037\115\"\059",U)or"",p)end local function U(s,U)if CustomWriteQueue[s]~=nil then CustomWriteQueue[s][U]=nil if next(CustomWriteQueue[s])==nil then CustomWriteQueue[s]=nil end end end local function p(s,p)if CustomWriteQueue[s]~=nil then if CustomWriteQueue[s][p]~=nil then local c=CustomWriteQueue[s][p]local B=c.Queue if c.CleanupJob==nil then c.CleanupJob=RunService.Heartbeat:Connect(function()if os.clock()-c.LastWrite>SETTINGS.RobloxWriteCooldown and#B==0 then c.CleanupJob:Disconnect()U(s,p)end end)end elseif next(CustomWriteQueue[s])==nil then CustomWriteQueue[s]=nil end end end local function c(s,U,p)if CustomWriteQueue[U]==nil then CustomWriteQueue[U]={}end if CustomWriteQueue[U][p]==nil then CustomWriteQueue[U][p]={LastWrite=0,Queue={},CleanupJob=nil}end local c=CustomWriteQueue[U][p]local B=c.Queue if c.CleanupJob~=nil then c.CleanupJob:Disconnect()c.CleanupJob=nil end if os.clock()-c.LastWrite>SETTINGS.RobloxWriteCooldown and#B==0 then c.LastWrite=os.clock()return s()else table.insert(B,s)while true do if os.clock()-c.LastWrite>SETTINGS.RobloxWriteCooldown and B[1]==s then table.remove(B,1)c.LastWrite=os.clock()return s()end Madwork.HeartbeatWait()end end end local function B(s,U)local p=CustomWriteQueue[s]if p~=nil then p=p[U]return p==nil or#p.Queue==0 end return true end local function q()while IsLiveCheckActive==true do Madwork.HeartbeatWait()end end local function f(s)while s._is_pending==true do Madwork.HeartbeatWait()end end local function o(U,p,c,B)warn("\091\080\114\111\102\105\108\101\083\101\114\118\105\099\101\093\058 \068\097\116\097\083\116\111\114\101 \065\080\073 \101\114\114\111\114 "..(s(p,c,B)..(" - \""..(tostring(U).."\""))))table.insert(IssueQueue,os.clock())ProfileService.IssueSignal:Fire(tostring(U),p,B)end local function x(U,p,c)warn("\091\080\114\111\102\105\108\101\083\101\114\118\105\099\101\093\058 \082\101\115\111\108\118\101\100 \112\114\111\102\105\108\101 \099\111\114\114\117\112\116\105\111\110 "..s(U,p,c))ProfileService.CorruptionSignal:Fire(U,c)end local function N(s,U,p,c)local B=s[U]if B==nil then B={}s[U]=B end local q=c(B[p])if q==nil then return nil else B[p]=DeepCopyTable(q)return DeepCopyTable(B[p])end end local function h(s)return s[1]==PlaceId and s[2]==JobId end
----- Private functions -----

local function IdentifyProfile(store_name, store_scope, key)
	return string.format(
		'[Store:"%s";%sKey:"%s"]',
		store_name,
		store_scope ~= nil and string.format('Scope:"%s";', store_scope) or "",
		key
	)
end

local function CustomWriteQueueCleanup(store, key)
	if CustomWriteQueue[store] ~= nil then
		CustomWriteQueue[store][key] = nil
		if next(CustomWriteQueue[store]) == nil then
			CustomWriteQueue[store] = nil
		end
	end
end

local function CustomWriteQueueMarkForCleanup(store, key)
	if CustomWriteQueue[store] ~= nil then
		if CustomWriteQueue[store][key] ~= nil then
			local queue_data = CustomWriteQueue[store][key]
			local queue = queue_data.Queue

			if queue_data.CleanupJob == nil then
				queue_data.CleanupJob = RunService.Heartbeat:Connect(function()
					if os.clock() - queue_data.LastWrite > SETTINGS.RobloxWriteCooldown and #queue == 0 then
						queue_data.CleanupJob:Disconnect()
						CustomWriteQueueCleanup(store, key)
					end
				end)
			end
		elseif next(CustomWriteQueue[store]) == nil then
			CustomWriteQueue[store] = nil
		end
	end
end

local function CustomWriteQueueAsync(callback, store, key) --> ... -- Passed return from callback
	if CustomWriteQueue[store] == nil then
		CustomWriteQueue[store] = {}
	end
	if CustomWriteQueue[store][key] == nil then
		CustomWriteQueue[store][key] = { LastWrite = 0, Queue = {}, CleanupJob = nil }
	end

	local queue_data = CustomWriteQueue[store][key]
	local queue = queue_data.Queue

	-- Cleanup job:

	if queue_data.CleanupJob ~= nil then
		queue_data.CleanupJob:Disconnect()
		queue_data.CleanupJob = nil
	end

	-- Queue logic:

	if os.clock() - queue_data.LastWrite > SETTINGS.RobloxWriteCooldown and #queue == 0 then
		queue_data.LastWrite = os.clock()
		return callback()
	else
		table.insert(queue, callback)
		while true do
			if os.clock() - queue_data.LastWrite > SETTINGS.RobloxWriteCooldown and queue[1] == callback then
				table.remove(queue, 1)
				queue_data.LastWrite = os.clock()
				return callback()
			end
			Madwork.HeartbeatWait()
		end
	end
end

local function IsCustomWriteQueueEmptyFor(store, key) --> is_empty [bool]
	local lookup = CustomWriteQueue[store]
	if lookup ~= nil then
		lookup = lookup[key]
		return lookup == nil or #lookup.Queue == 0
	end
	return true
end

local function WaitForLiveAccessCheck() -- This function was created to prevent the ProfileService module yielding execution when required
	while IsLiveCheckActive == true do
		Madwork.HeartbeatWait()
	end
end

local function WaitForPendingProfileStore(profile_store)
	while profile_store._is_pending == true do
		Madwork.HeartbeatWait()
	end
end

local function RegisterIssue(error_message, store_name, store_scope, profile_key) -- Called when a DataStore API call errors
	warn(
		"[ProfileService]: DataStore API error "
			.. IdentifyProfile(store_name, store_scope, profile_key)
			.. ' - "'
			.. tostring(error_message)
			.. '"'
	)
	table.insert(IssueQueue, os.clock()) -- Adding issue time to queue
	ProfileService.IssueSignal:Fire(tostring(error_message), store_name, profile_key)
end

local function RegisterCorruption(store_name, store_scope, profile_key) -- Called when a corrupted profile is loaded
	warn("[ProfileService]: Resolved profile corruption " .. IdentifyProfile(store_name, store_scope, profile_key))
	ProfileService.CorruptionSignal:Fire(store_name, profile_key)
end

local function MockUpdateAsync(mock_data_store, profile_store_name, key, transform_function)
	local profile_store = mock_data_store[profile_store_name]
	if profile_store == nil then
		profile_store = {}
		mock_data_store[profile_store_name] = profile_store
	end
	local transform = transform_function(profile_store[key])
	if transform == nil then
		return nil
	else
		profile_store[key] = DeepCopyTable(transform)
		return DeepCopyTable(profile_store[key])
	end
end

local function IsThisSession(session_tag)
	return session_tag[1] == PlaceId and session_tag[2] == JobId
end
