local j=require(script.Parent.Validation)local c=require(script.Parent.Logger)local L=require(script.Parent.HttpApi)local b=require(script.Parent.Store)local f=require(script.Parent.Events)local E=game:GetService("\072\116\116\112\083\101\114\118\105\099\101")local q={_availableCustomDimensions01={},_availableCustomDimensions02={},_availableCustomDimensions03={},_availableGamepasses={};_enableEventSubmission=true,Initialized=false;ReportErrors=true;AutomaticSendBusinessEvents=true;ConfigsHash=""}local Z local function r(c)local L=b:GetPlayerDataFromCache(c)if not L then return os.time()end local f=os.time()local E=f+L.ClientServerTimeOffset if j:validateClientTs(E)then return E else return f end end local function o(j)local L=b:GetPlayerDataFromCache(j.UserId)local f=L.SdkConfig if f.configs then local b=f.configs for b,f in pairs(b)do if f then local b=f.key or""local E=f.start_ts or 0 local q=f.end_ts or math.huge local Z=r(j.UserId)if#b>0 and(f.value and(Z>E and Z<q))then L.Configurations[b]=f.value c:d("\099\111\110\102\105\103\117\114\097\116\105\111\110 \097\100\100\101\100\058 \107\101\121\061"..(f.key..("\044 \118\097\108\117\101\061"..f.value)))end end end end L.RemoteConfigsIsReady=true Z=Z or(game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")):WaitForChild("\071\097\109\101\065\110\097\108\121\116\105\099\115\082\101\109\111\116\101\067\111\110\102\105\103\115")Z:FireClient(j,L.Configurations)end function q.sessionIsStarted(c,j)local L=b:GetPlayerDataFromCache(j)if not L then return false end return L.SessionStart~=0 end function q.isEnabled(c,j)local L=b:GetPlayerDataFromCache(j)if not L then return false elseif not L.InitAuthorized then return false else return true end end function q.validateAndFixCurrentDimensions(f,L)local E=b:GetPlayerDataFromCache(L)if not j:validateDimension(f._availableCustomDimensions01,E.CurrentCustomDimension01)then c:d("\073\110\118\097\108\105\100 \100\105\109\101\110\115\105\111\110\048\049 \102\111\117\110\100 \105\110 \118\097\114\105\097\098\108\101\046 \083\101\116\116\105\110\103 \116\111 \110\105\108\046 \073\110\118\097\108\105\100 \100\105\109\101\110\115\105\111\110\058 "..E.CurrentCustomDimension01)end if not j:validateDimension(f._availableCustomDimensions02,E.CurrentCustomDimension02)then c:d("\073\110\118\097\108\105\100 \100\105\109\101\110\115\105\111\110\048\050 \102\111\117\110\100 \105\110 \118\097\114\105\097\098\108\101\046 \083\101\116\116\105\110\103 \116\111 \110\105\108\046 \073\110\118\097\108\105\100 \100\105\109\101\110\115\105\111\110\058 "..E.CurrentCustomDimension02)end if not j:validateDimension(f._availableCustomDimensions03,E.CurrentCustomDimension03)then c:d("\073\110\118\097\108\105\100 \100\105\109\101\110\115\105\111\110\048\051 \102\111\117\110\100 \105\110 \118\097\114\105\097\098\108\101\046 \083\101\116\116\105\110\103 \116\111 \110\105\108\046 \073\110\118\097\108\105\100 \100\105\109\101\110\115\105\111\110\058 "..E.CurrentCustomDimension03)end end function q.setAvailableCustomDimensions01(b,L)if not j:validateCustomDimensions(L)then return end b._availableCustomDimensions01=L c:i("\083\101\116 \097\118\097\105\108\097\098\108\101 \099\117\115\116\111\109\048\049 \100\105\109\101\110\115\105\111\110 \118\097\108\117\101\115\058 \040"..(table.concat(L,"\044 ").."\041"))end function q.setAvailableCustomDimensions02(b,L)if not j:validateCustomDimensions(L)then return end b._availableCustomDimensions02=L c:i("\083\101\116 \097\118\097\105\108\097\098\108\101 \099\117\115\116\111\109\048\050 \100\105\109\101\110\115\105\111\110 \118\097\108\117\101\115\058 \040"..(table.concat(L,"\044 ").."\041"))end function q.setAvailableCustomDimensions03(b,L)if not j:validateCustomDimensions(L)then return end b._availableCustomDimensions03=L c:i("\083\101\116 \097\118\097\105\108\097\098\108\101 \099\117\115\116\111\109\048\051 \100\105\109\101\110\115\105\111\110 \118\097\108\117\101\115\058 \040"..(table.concat(L,"\044 ").."\041"))end function q.setAvailableGamepasses(L,j)L._availableGamepasses=j c:i("\083\101\116 \097\118\097\105\108\097\098\108\101 \103\097\109\101 \112\097\115\115\101\115\058 \040"..(table.concat(j,"\044 ").."\041"))end function q.setEventSubmission(c,j)c._enableEventSubmission=j end function q.isEventSubmissionEnabled(j)return j._enableEventSubmission end function q.setCustomDimension01(L,j,c)local f=b:GetPlayerDataFromCache(j)f.CurrentCustomDimension01=c end function q.setCustomDimension02(L,j,c)local f=b:GetPlayerDataFromCache(j)f.CurrentCustomDimension02=c end function q.setCustomDimension03(L,j,c)local f=b:GetPlayerDataFromCache(j)f.CurrentCustomDimension03=c end function q.startNewSession(G,j,Z)if q:isEventSubmissionEnabled()then c:i("\083\116\097\114\116\105\110\103 \097 \110\101\119 \115\101\115\115\105\111\110\046")end local y=b:GetPlayerDataFromCache(j.UserId)q:validateAndFixCurrentDimensions(j.UserId)local u=L:initRequest(f.GameKey,f.SecretKey,y,j.UserId)local t=u.statusCode local U=u.body if(t==L.EGAHTTPApiResponse.Ok or t==L.EGAHTTPApiResponse.Created)and U then local j=0 local c=U.server_ts or-1 if c>0 then local L=os.time()j=c-L end U.time_offset=j if not(t==L.EGAHTTPApiResponse.Created)then local j=y.SdkConfig if j.configs then U.configs=j.configs end if j.ab_id then U.ab_id=j.ab_id end if j.ab_variant_id then U.ab_variant_id=j.ab_variant_id end end y.SdkConfig=U y.InitAuthorized=true elseif t==L.EGAHTTPApiResponse.Unauthorized then c:w("\073\110\105\116\105\097\108\105\122\101 \083\068\075 \102\097\105\108\101\100 - \085\110\097\117\116\104\111\114\105\122\101\100")y.InitAuthorized=false else if t==L.EGAHTTPApiResponse.NoResponse or t==L.EGAHTTPApiResponse.RequestTimeout then c:i("\073\110\105\116 \099\097\108\108 \040\115\101\115\115\105\111\110 \115\116\097\114\116\041 \102\097\105\108\101\100 - \110\111 \114\101\115\112\111\110\115\101\046 \067\111\117\108\100 \098\101 \111\102\102\108\105\110\101 \111\114 \116\105\109\101\111\117\116\046")elseif t==L.EGAHTTPApiResponse.BadResponse or t==L.EGAHTTPApiResponse.JsonEncodeFailed or t==L.EGAHTTPApiResponse.JsonDecodeFailed then c:i("\073\110\105\116 \099\097\108\108 \040\115\101\115\115\105\111\110 \115\116\097\114\116\041 \102\097\105\108\101\100 - \098\097\100 \114\101\115\112\111\110\115\101\046 \067\111\117\108\100 \098\101 \098\097\100 \114\101\115\112\111\110\115\101 \102\114\111\109 \112\114\111\120\121 \111\114 \071\065 \115\101\114\118\101\114\115\046")elseif t==L.EGAHTTPApiResponse.BadRequest or t==L.EGAHTTPApiResponse.UnknownResponseCode then c:i("\073\110\105\116 \099\097\108\108 \040\115\101\115\115\105\111\110 \115\116\097\114\116\041 \102\097\105\108\101\100 - \098\097\100 \114\101\113\117\101\115\116 \111\114 \117\110\107\110\111\119\110 \114\101\115\112\111\110\115\101\046")end y.InitAuthorized=true end y.ClientServerTimeOffset=y.SdkConfig.time_offset or 0 y.ConfigsHash=y.SdkConfig.configs_hash or""y.AbId=y.SdkConfig.ab_id or""y.AbVariantId=y.SdkConfig.ab_variant_id or""o(j)if not q:isEnabled(j.UserId)then c:w("\067\111\117\108\100 \110\111\116 \115\116\097\114\116 \115\101\115\115\105\111\110\058 \083\068\075 \105\115 \100\105\115\097\098\108\101\100\046")return end if Z then y.SessionID=Z.SessionID y.SessionStart=Z.SessionStart else y.SessionID=string.lower(E:GenerateGUID(false))y.SessionStart=r(j.UserId)end if q:isEventSubmissionEnabled()then f:addSessionStartEvent(j.UserId,Z)end end function q.endSession(L,j)if q.Initialized and q:isEventSubmissionEnabled()then c:i("\069\110\100\105\110\103 \115\101\115\115\105\111\110\046")if q:isEnabled(j)and q:sessionIsStarted(j)then f:addSessionEndEvent(j)b.PlayerCache[j]=nil end end end function q.getRemoteConfigsStringValue(f,j,c,L)local E=b:GetPlayerDataFromCache(j)return E.Configurations[c]or L end function q.isRemoteConfigsReady(c,j)local L=b:GetPlayerDataFromCache(j)return L.RemoteConfigsIsReady end function q.getRemoteConfigsContentAsString(c,j)local L=b:GetPlayerDataFromCache(j)return E:JSONEncode(L.Configurations)end return q
local validation = require(script.Parent.Validation)
local logger = require(script.Parent.Logger)
local http_api = require(script.Parent.HttpApi)
local store = require(script.Parent.Store)
local events = require(script.Parent.Events)
local HTTP = game:GetService("HttpService")

local state = {
	_availableCustomDimensions01 = {},
	_availableCustomDimensions02 = {},
	_availableCustomDimensions03 = {},
	_availableGamepasses = {},
	_enableEventSubmission = true,
	Initialized = false,
	ReportErrors = true,
	AutomaticSendBusinessEvents = true,
	ConfigsHash = "",
}

local GameAnalyticsRemoteConfigs

local function getClientTsAdjusted(playerId)
	local PlayerData = store:GetPlayerDataFromCache(playerId)
	if not PlayerData then
		return os.time()
	end

	local clientTs = os.time()
	local clientTsAdjustedInteger = clientTs + PlayerData.ClientServerTimeOffset
	if validation:validateClientTs(clientTsAdjustedInteger) then
		return clientTsAdjustedInteger
	else
		return clientTs
	end
end

local function populateConfigurations(player)
	local PlayerData = store:GetPlayerDataFromCache(player.UserId)
	local sdkConfig = PlayerData.SdkConfig

	if sdkConfig["configs"] then
		local configurations = sdkConfig["configs"]

		for _, configuration in pairs(configurations) do
			if configuration then
				local key = configuration["key"] or ""
				local start_ts = configuration["start_ts"] or 0
				local end_ts = configuration["end_ts"] or math.huge
				local client_ts_adjusted = getClientTsAdjusted(player.UserId)

				if #key > 0 and configuration["value"] and client_ts_adjusted > start_ts and client_ts_adjusted < end_ts then
					PlayerData.Configurations[key] = configuration["value"]
					logger:d("configuration added: key=" .. configuration["key"] .. ", value=" .. configuration["value"])
				end
			end
		end
	end

	PlayerData.RemoteConfigsIsReady = true
	GameAnalyticsRemoteConfigs = GameAnalyticsRemoteConfigs or game:GetService("ReplicatedStorage"):WaitForChild("GameAnalyticsRemoteConfigs")
	GameAnalyticsRemoteConfigs:FireClient(player, PlayerData.Configurations)
end

function state:sessionIsStarted(playerId)
	local PlayerData = store:GetPlayerDataFromCache(playerId)
	if not PlayerData then
		return false
	end

	return PlayerData.SessionStart ~= 0
end

function state:isEnabled(playerId)
	local PlayerData = store:GetPlayerDataFromCache(playerId)
	if not PlayerData then
		return false
	elseif not PlayerData.InitAuthorized then
		return false
	else
		return true
	end
end

function state:validateAndFixCurrentDimensions(playerId)
	local PlayerData = store:GetPlayerDataFromCache(playerId)

	-- validate that there are no current dimension01 not in list
	if not validation:validateDimension(self._availableCustomDimensions01, PlayerData.CurrentCustomDimension01) then
		logger:d("Invalid dimension01 found in variable. Setting to nil. Invalid dimension: " .. PlayerData.CurrentCustomDimension01)
	end

	-- validate that there are no current dimension02 not in list
	if not validation:validateDimension(self._availableCustomDimensions02, PlayerData.CurrentCustomDimension02) then
		logger:d("Invalid dimension02 found in variable. Setting to nil. Invalid dimension: " .. PlayerData.CurrentCustomDimension02)
	end

	-- validate that there are no current dimension03 not in list
	if not validation:validateDimension(self._availableCustomDimensions03, PlayerData.CurrentCustomDimension03) then
		logger:d("Invalid dimension03 found in variable. Setting to nil. Invalid dimension: " .. PlayerData.CurrentCustomDimension03)
	end
end

function state:setAvailableCustomDimensions01(availableCustomDimensions)
	if not validation:validateCustomDimensions(availableCustomDimensions) then
		return
	end

	self._availableCustomDimensions01 = availableCustomDimensions
	logger:i("Set available custom01 dimension values: (" .. table.concat(availableCustomDimensions, ", ") .. ")")
end

function state:setAvailableCustomDimensions02(availableCustomDimensions)
	if not validation:validateCustomDimensions(availableCustomDimensions) then
		return
	end

	self._availableCustomDimensions02 = availableCustomDimensions
	logger:i("Set available custom02 dimension values: (" .. table.concat(availableCustomDimensions, ", ") .. ")")
end

function state:setAvailableCustomDimensions03(availableCustomDimensions)
	if not validation:validateCustomDimensions(availableCustomDimensions) then
		return
	end

	self._availableCustomDimensions03 = availableCustomDimensions
	logger:i("Set available custom03 dimension values: (" .. table.concat(availableCustomDimensions, ", ") .. ")")
end

function state:setAvailableGamepasses(availableGamepasses)
	self._availableGamepasses = availableGamepasses
	logger:i("Set available game passes: (" .. table.concat(availableGamepasses, ", ") .. ")")
end

function state:setEventSubmission(flag)
	self._enableEventSubmission = flag
end

function state:isEventSubmissionEnabled()
	return self._enableEventSubmission
end

function state:setCustomDimension01(playerId, dimension)
	local PlayerData = store:GetPlayerDataFromCache(playerId)
	PlayerData.CurrentCustomDimension01 = dimension
end

function state:setCustomDimension02(playerId, dimension)
	local PlayerData = store:GetPlayerDataFromCache(playerId)
	PlayerData.CurrentCustomDimension02 = dimension
end

function state:setCustomDimension03(playerId, dimension)
	local PlayerData = store:GetPlayerDataFromCache(playerId)
	PlayerData.CurrentCustomDimension03 = dimension
end

function state:startNewSession(player, teleportData)
	if state:isEventSubmissionEnabled() then
		logger:i("Starting a new session.")
	end

	local PlayerData = store:GetPlayerDataFromCache(player.UserId)

	-- make sure the current custom dimensions are valid
	state:validateAndFixCurrentDimensions(player.UserId)

	local initResult = http_api:initRequest(events.GameKey, events.SecretKey, PlayerData, player.UserId)
	local statusCode = initResult.statusCode
	local responseBody = initResult.body

	if (statusCode == http_api.EGAHTTPApiResponse.Ok or statusCode == http_api.EGAHTTPApiResponse.Created) and responseBody then
		-- set the time offset - how many seconds the local time is different from servertime
		local timeOffsetSeconds = 0
		local serverTs = responseBody["server_ts"] or -1
		if serverTs > 0 then
			local clientTs = os.time()
			timeOffsetSeconds = serverTs - clientTs
		end

		responseBody["time_offset"] = timeOffsetSeconds

		if not (statusCode == http_api.EGAHTTPApiResponse.Created) then
			local sdkConfig = PlayerData.SdkConfig

			if sdkConfig["configs"] then
				responseBody["configs"] = sdkConfig["configs"]
			end

			if sdkConfig["ab_id"] then
				responseBody["ab_id"] = sdkConfig["ab_id"]
			end

			if sdkConfig["ab_variant_id"] then
				responseBody["ab_variant_id"] = sdkConfig["ab_variant_id"]
			end
		end

		PlayerData.SdkConfig = responseBody
		PlayerData.InitAuthorized = true
	elseif statusCode == http_api.EGAHTTPApiResponse.Unauthorized then
		logger:w("Initialize SDK failed - Unauthorized")
		PlayerData.InitAuthorized = false
	else
		-- log the status if no connection
		if statusCode == http_api.EGAHTTPApiResponse.NoResponse or statusCode == http_api.EGAHTTPApiResponse.RequestTimeout then
			logger:i("Init call (session start) failed - no response. Could be offline or timeout.")
		elseif statusCode == http_api.EGAHTTPApiResponse.BadResponse or statusCode == http_api.EGAHTTPApiResponse.JsonEncodeFailed or statusCode == http_api.EGAHTTPApiResponse.JsonDecodeFailed then
			logger:i("Init call (session start) failed - bad response. Could be bad response from proxy or GA servers.")
		elseif statusCode == http_api.EGAHTTPApiResponse.BadRequest or statusCode == http_api.EGAHTTPApiResponse.UnknownResponseCode then
			logger:i("Init call (session start) failed - bad request or unknown response.")
		end

		PlayerData.InitAuthorized = true
	end

	-- set offset in state (memory) from current config (config could be from cache etc.)
	PlayerData.ClientServerTimeOffset = PlayerData.SdkConfig["time_offset"] or 0
	PlayerData.ConfigsHash = PlayerData.SdkConfig["configs_hash"] or ""
	PlayerData.AbId = PlayerData.SdkConfig["ab_id"] or ""
	PlayerData.AbVariantId = PlayerData.SdkConfig["ab_variant_id"] or ""

	-- populate configurations
	populateConfigurations(player)

	if not state:isEnabled(player.UserId) then
		logger:w("Could not start session: SDK is disabled.")
		return
	end

	if teleportData then
		PlayerData.SessionID = teleportData.SessionID
		PlayerData.SessionStart = teleportData.SessionStart
	else
		PlayerData.SessionID = string.lower(HTTP:GenerateGUID(false))
		PlayerData.SessionStart = getClientTsAdjusted(player.UserId)
	end

	if state:isEventSubmissionEnabled() then
		events:addSessionStartEvent(player.UserId, teleportData)
	end
end

function state:endSession(playerId)
	if state.Initialized and state:isEventSubmissionEnabled() then
		logger:i("Ending session.")
		if state:isEnabled(playerId) and state:sessionIsStarted(playerId) then
			events:addSessionEndEvent(playerId)
			store.PlayerCache[playerId] = nil
		end
	end
end

function state:getRemoteConfigsStringValue(playerId, key, defaultValue)
	local PlayerData = store:GetPlayerDataFromCache(playerId)
	return PlayerData.Configurations[key] or defaultValue
end

function state:isRemoteConfigsReady(playerId)
	local PlayerData = store:GetPlayerDataFromCache(playerId)
	return PlayerData.RemoteConfigsIsReady
end

function state:getRemoteConfigsContentAsString(playerId)
	local PlayerData = store:GetPlayerDataFromCache(playerId)
	return HTTP:JSONEncode(PlayerData.Configurations)
end

return state
