local k=require(script.Parent.Parent.Parent.MockDataStoreService)local d=require(script.Parent.Parent.Parent.Promise)local N=require(script.Parent.Parent.Error)local y=game:GetService("\082\117\110\083\101\114\118\105\099\101")local u=require(script.Parent.DataStoreLayer)local h={GetAsync=Enum.DataStoreRequestType.GetAsync;UpdateAsync=Enum.DataStoreRequestType.UpdateAsync,RemoveAsync=Enum.DataStoreRequestType.SetIncrementAsync,SetAsync=Enum.DataStoreRequestType.SetIncrementAsync}local n={_queue={};_dataStores={}}function n._perform(c,C,...)local B=h[c]if k:GetRequestBudgetForRequestType(B)>0 then return u.perform(c,C,...)end if n._queue[B]==nil then n._queue[B]={};(coroutine.wrap(function()y.Heartbeat:Wait()while#n._queue[B]>0 do local d=table.remove(n._queue[B],1)while k:GetRequestBudgetForRequestType(B)==0 do y.Heartbeat:Wait()end local h,c=pcall(u.perform,unpack(d.args))if h then d.resolve(c)else d.reject(N.new({kind=N.Kind.DataStoreError,error=c}))end end n._queue[B]=nil end))()end local o={c,C;...}local t=d.new(function(k,d)table.insert(n._queue[B],{args=o;resolve=k,reject=d})end)return t:expect()end function n.update(k,d,N)return n._perform("\085\112\100\097\116\101\065\115\121\110\099",k,d,N)end function n.read(k,d)return n._perform("\071\101\116\065\115\121\110\099",k,d)end function n.write(k,d,N)return n._perform("\083\101\116\065\115\121\110\099",k,d,N)end function n.remove(k,d)return n._perform("\082\101\109\111\118\101\065\115\121\110\099",k,d)end return n
local DataStoreService = require(script.Parent.Parent.Parent.MockDataStoreService)
local Promise = require(script.Parent.Parent.Parent.Promise)
local Error = require(script.Parent.Parent.Error)
local RunService = game:GetService("RunService")

local DataStoreLayer = require(script.Parent.DataStoreLayer)

local METHOD_RESOURCE_MAP = {
	GetAsync = Enum.DataStoreRequestType.GetAsync;
	UpdateAsync = Enum.DataStoreRequestType.UpdateAsync,
	RemoveAsync = Enum.DataStoreRequestType.SetIncrementAsync,
	SetAsync = Enum.DataStoreRequestType.SetIncrementAsync,
}

local ThrottleLayer = {
	_queue = {};
	_dataStores = {};
}

function ThrottleLayer._perform(methodName, collectionName, ...)
	local resource = METHOD_RESOURCE_MAP[methodName]

	if DataStoreService:GetRequestBudgetForRequestType(resource) > 0 then
		return DataStoreLayer.perform(methodName, collectionName, ...)
	end

	if ThrottleLayer._queue[resource] == nil then
		ThrottleLayer._queue[resource] = {}

		coroutine.wrap(function()
			RunService.Heartbeat:Wait()
			while #ThrottleLayer._queue[resource] > 0 do
				local request = table.remove(ThrottleLayer._queue[resource], 1)

				while DataStoreService:GetRequestBudgetForRequestType(resource) == 0 do
					RunService.Heartbeat:Wait()
				end

				local ok, result = pcall(DataStoreLayer.perform, unpack(request.args))
				if ok then
					request.resolve(result)
				else
					request.reject(Error.new({
						kind = Error.Kind.DataStoreError,
						error = result
					}))
				end
			end

			ThrottleLayer._queue[resource] = nil
		end)()
	end

	local args = { methodName, collectionName, ... }
	local promise = Promise.new(function(resolve, reject)
		table.insert(ThrottleLayer._queue[resource], {
			args = args,
			resolve = resolve,
			reject = reject
		})
	end)

	return promise:expect()
end

function ThrottleLayer.update(collection, key, callback)
	return ThrottleLayer._perform("UpdateAsync", collection, key, callback)
end

function ThrottleLayer.read(collection, key)
	return ThrottleLayer._perform("GetAsync", collection, key)
end

function ThrottleLayer.write(collection, key, value)
	return ThrottleLayer._perform("SetAsync", collection, key, value)
end

function ThrottleLayer.remove(collection, key)
	return ThrottleLayer._perform("RemoveAsync", collection, key)
end

return ThrottleLayer