local V=game:GetService("\082\117\110\083\101\114\118\105\099\101")local G=require(script.Parent.Validation)local v=require(script.Parent.Version)local B=require(script.HashLib)local g={protocol="\104\116\116\112\115";hostName="\097\112\105\046\103\097\109\101\097\110\097\108\121\116\105\099\115\046\099\111\109";version="\118\050";remoteConfigsVersion="\118\049",initializeUrlPath="\105\110\105\116",eventsUrlPath="\101\118\101\110\116\115";EGAHTTPApiResponse={NoResponse=0,BadResponse=1,RequestTimeout=2;JsonEncodeFailed=3;JsonDecodeFailed=4;InternalServerError=5;BadRequest=6;Unauthorized=7;UnknownResponseCode=8,Ok=9;Created=10}}local m=game:GetService("\072\116\116\112\083\101\114\118\105\099\101")local X=require(script.Parent.Logger)local F=(V:IsStudio()and"\104\116\116\112"or g.protocol)..("\058\047\047"..((V:IsStudio()and"\115\097\110\100\098\111\120-"or"")..(g.hostName..("\047"..g.version))))local P=(V:IsStudio()and"\104\116\116\112"or g.protocol)..("\058\047\047"..((V:IsStudio()and"\115\097\110\100\098\111\120-"or"")..(g.hostName..("\047\114\101\109\111\116\101\095\099\111\110\102\105\103\115\047"..g.remoteConfigsVersion))))local Y={}local function x(V,G)local B={user_id=tostring(G),sdk_version="\114\111\098\108\111\120 "..v.SdkVersion;os_version=V.OS;platform=V.Platform,build=nil;session_num=V.Sessions;random_salt=V.Sessions}return B end local function j(G,v)if not v then X:w("\069\114\114\111\114 \101\110\099\111\100\105\110\103\044 \105\110\118\097\108\105\100 \083\101\099\114\101\116\075\101\121")return end local g=B.hmac(B.sha256,V:IsStudio()and"\049\054\056\049\051\097\049\050\102\055\049\056\098\099\053\099\054\050\048\102\053\054\057\052\052\101\049\097\098\099\051\101\097\049\051\099\099\098\097\099"or v,G,true)return B.base64_encode(g)end local function Z(V,G)local v=V.StatusCode local B=V.Body if not B or#B==0 then X:d(G..(" \114\101\113\117\101\115\116\046 \102\097\105\108\101\100\046 \077\105\103\104\116 \098\101 \110\111 \099\111\110\110\101\099\116\105\111\110\046 \083\116\097\116\117\115 \099\111\100\101\058 "..tostring(v)))return g.EGAHTTPApiResponse.NoResponse end if v==200 then return g.EGAHTTPApiResponse.Ok elseif v==201 then return g.EGAHTTPApiResponse.Created elseif v==0 or v==401 then X:d(G.." \114\101\113\117\101\115\116\046 \052\048\049 - \085\110\097\117\116\104\111\114\105\122\101\100\046")return g.EGAHTTPApiResponse.Unauthorized elseif v==400 then X:d(G.." \114\101\113\117\101\115\116\046 \052\048\048 - \066\097\100 \082\101\113\117\101\115\116\046")return g.EGAHTTPApiResponse.BadRequest elseif v==500 then X:d(G.." \114\101\113\117\101\115\116\046 \053\048\048 - \073\110\116\101\114\110\097\108 \083\101\114\118\101\114 \069\114\114\111\114\046")return g.EGAHTTPApiResponse.InternalServerError else return g.EGAHTTPApiResponse.UnknownResponseCode end end function g.initRequest(d,v,B,Y,l)local M=P..("\047"..(g.initializeUrlPath..("\063\103\097\109\101\095\107\101\121\061"..(v..("\038\105\110\116\101\114\118\097\108\095\115\101\099\111\110\100\115\061\048\038\099\111\110\102\105\103\115\095\104\097\115\104\061"..(Y.ConfigsHash or""))))))if V:IsStudio()then M=F..("\047\053\099\054\098\099\098\053\052\048\050\050\048\052\050\052\057\052\051\055\102\098\053\097\055\097\056\048\097\052\057\053\057\047"..d.initializeUrlPath)end X:d("\083\101\110\100\105\110\103 \'\105\110\105\116\' \085\082\076\058 "..M)local R=m:JSONEncode(x(Y,l))local K=j(R,B)local o local e,k=pcall(function()o=m:RequestAsync({Url=M;Method="\080\079\083\084";Headers={Authorization=K,["\067\111\110\116\101\110\116-\084\121\112\101"]="\097\112\112\108\105\099\097\116\105\111\110\047\106\115\111\110"},Body=R})end)if not e then X:d("\070\097\105\108\101\100 \073\110\105\116 \067\097\108\108\046 \101\114\114\111\114\058 "..k)return{statusCode=g.EGAHTTPApiResponse.UnknownResponseCode,body=nil}end X:d("\105\110\105\116 \114\101\113\117\101\115\116 \099\111\110\116\101\110\116\058 "..o.Body)local I=Z(o,"\073\110\105\116")if I~=g.EGAHTTPApiResponse.Ok and(I~=g.EGAHTTPApiResponse.Created and I~=g.EGAHTTPApiResponse.BadRequest)then X:d("\070\097\105\108\101\100 \073\110\105\116 \067\097\108\108\046 \085\082\076\058 "..(M..("\044 \074\083\079\078\083\116\114\105\110\103\058 "..(R..("\044 \065\117\116\104\111\114\105\122\097\116\105\111\110\058 "..K)))))return{statusCode=I,body=nil}end local Q e=ypcall(function()Q=m:JSONDecode(o.Body)end)if not e then X:d("\070\097\105\108\101\100 \073\110\105\116 \067\097\108\108\046 \074\115\111\110 \100\101\099\111\100\105\110\103 \102\097\105\108\101\100\058 "..k)return{statusCode=g.EGAHTTPApiResponse.JsonDecodeFailed;body=nil}end if I==g.EGAHTTPApiResponse.BadRequest then X:d("\070\097\105\108\101\100 \073\110\105\116 \067\097\108\108\046 \066\097\100 \114\101\113\117\101\115\116\046 \082\101\115\112\111\110\115\101\058 "..o.Body)return{statusCode=I;body=nil}end local a=G:validateAndCleanInitRequestResponse(Q,I==g.EGAHTTPApiResponse.Created)if not a then return{statusCode=g.EGAHTTPApiResponse.BadResponse,body=nil}end return{statusCode=I;body=Q}end function g.sendEventsInArray(P,G,v,B)if not B or#B==0 then X:d("\115\101\110\100\069\118\101\110\116\115\073\110\065\114\114\097\121 \099\097\108\108\101\100 \119\105\116\104 \109\105\115\115\105\110\103 \101\118\101\110\116\065\114\114\097\121")return end local Y=F..("\047"..(G..("\047"..P.eventsUrlPath)))if V:IsStudio()then Y=F..("\047\053\099\054\098\099\098\053\052\048\050\050\048\052\050\052\057\052\051\055\102\098\053\097\055\097\056\048\097\052\057\053\057\047"..P.eventsUrlPath)end X:d("\083\101\110\100\105\110\103 \'\101\118\101\110\116\115\' \085\082\076\058 "..Y)local x=m:JSONEncode(B)local l=j(x,v)local d local M,R=pcall(function()d=m:RequestAsync({Url=Y;Method="\080\079\083\084",Headers={Authorization=l;["\067\111\110\116\101\110\116-\084\121\112\101"]="\097\112\112\108\105\099\097\116\105\111\110\047\106\115\111\110"},Body=x})end)if not M then X:d("\070\097\105\108\101\100 \069\118\101\110\116\115 \067\097\108\108\046 \101\114\114\111\114\058 "..R)return{statusCode=g.EGAHTTPApiResponse.UnknownResponseCode,body=nil}end X:d("\098\111\100\121\058 "..d.Body)local K=Z(d,"\069\118\101\110\116\115")if K~=g.EGAHTTPApiResponse.Ok and(K~=g.EGAHTTPApiResponse.Created and K~=g.EGAHTTPApiResponse.BadRequest)then X:d("\070\097\105\108\101\100 \069\118\101\110\116\115 \067\097\108\108\046 \085\082\076\058 "..(Y..("\044 \074\083\079\078\083\116\114\105\110\103\058 "..(x..("\044 \065\117\116\104\111\114\105\122\097\116\105\111\110\058 "..l)))))return{statusCode=K;body=nil}end local o ypcall(function()o=m:JSONDecode(d.Body)end)if not o then X:d("\070\097\105\108\101\100 \069\118\101\110\116\115 \067\097\108\108\046 \074\115\111\110 \100\101\099\111\100\105\110\103 \102\097\105\108\101\100")return{statusCode=g.EGAHTTPApiResponse.JsonDecodeFailed,body=nil}end if K==g.EGAHTTPApiResponse.BadRequest then X:d("\070\097\105\108\101\100 \069\118\101\110\116\115 \067\097\108\108\046 \066\097\100 \114\101\113\117\101\115\116\046 \082\101\115\112\111\110\115\101\058 "..d.Body)return{statusCode=K,body=nil}end return{statusCode=g.EGAHTTPApiResponse.Ok,body=o}end return g
local RunService = game:GetService("RunService")
local validation = require(script.Parent.Validation)
local version = require(script.Parent.Version)

local HashLib = require(script.HashLib)

local http_api = {
	protocol = "https",
	hostName = "api.gameanalytics.com",
	version = "v2",
	remoteConfigsVersion = "v1",
	initializeUrlPath = "init",
	eventsUrlPath = "events",
	EGAHTTPApiResponse = {
		NoResponse = 0,
		BadResponse = 1,
		RequestTimeout = 2,
		JsonEncodeFailed = 3,
		JsonDecodeFailed = 4,
		InternalServerError = 5,
		BadRequest = 6,
		Unauthorized = 7,
		UnknownResponseCode = 8,
		Ok = 9,
		Created = 10,
	},
}

local HTTP = game:GetService("HttpService")
local logger = require(script.Parent.Logger)
local baseUrl = (RunService:IsStudio() and "http" or http_api.protocol) .. "://" .. (RunService:IsStudio() and "sandbox-" or "") .. http_api.hostName .. "/" .. http_api.version
local remoteConfigsBaseUrl = (RunService:IsStudio() and "http" or http_api.protocol) .. "://" .. (RunService:IsStudio() and "sandbox-" or "") .. http_api.hostName .. "/remote_configs/" .. http_api.remoteConfigsVersion

local Encoding = {}

local function getInitAnnotations(playerData, playerId)
	local initAnnotations = {
		["user_id"] = tostring(playerId),
		["sdk_version"] = "roblox " .. version.SdkVersion,
		["os_version"] = playerData.OS,
		["platform"] = playerData.Platform,
		["build"] = nil,
		["session_num"] = playerData.Sessions,
		["random_salt"] = playerData.Sessions,
	}

	return initAnnotations
end

local function encode(payload, secretKey)
	--Validate
	if not secretKey then
		logger:w("Error encoding, invalid SecretKey")
		return
	end

	--Encode
	local payloadHmac = HashLib.hmac(
		HashLib.sha256,
		RunService:IsStudio() and "16813a12f718bc5c620f56944e1abc3ea13ccbac" or secretKey,
		payload,
		true
	)

	return HashLib.base64_encode(payloadHmac)
end

local function processRequestResponse(response, requestId)
	local statusCode = response.StatusCode
	local body = response.Body

	if not body or #body == 0 then
		logger:d(requestId .. " request. failed. Might be no connection. Status code: " .. tostring(statusCode))
		return http_api.EGAHTTPApiResponse.NoResponse
	end

	if statusCode == 200 then
		return http_api.EGAHTTPApiResponse.Ok
	elseif statusCode == 201 then
			return http_api.EGAHTTPApiResponse.Created
	elseif statusCode == 0 or statusCode == 401 then
		logger:d(requestId .. " request. 401 - Unauthorized.")
		return http_api.EGAHTTPApiResponse.Unauthorized
	elseif statusCode == 400 then
		logger:d(requestId .. " request. 400 - Bad Request.")
		return http_api.EGAHTTPApiResponse.BadRequest
	elseif statusCode == 500 then
		logger:d(requestId .. " request. 500 - Internal Server Error.")
		return http_api.EGAHTTPApiResponse.InternalServerError
	else
		return http_api.EGAHTTPApiResponse.UnknownResponseCode
	end
end

function http_api:initRequest(gameKey, secretKey, playerData, playerId)
	local url = remoteConfigsBaseUrl .. "/" .. http_api.initializeUrlPath .. "?game_key=" .. gameKey .. "&interval_seconds=0&configs_hash=" .. (playerData.ConfigsHash or "")
	if RunService:IsStudio() then
		url = baseUrl .. "/5c6bcb5402204249437fb5a7a80a4959/" .. self.initializeUrlPath
	end

	logger:d("Sending 'init' URL: " .. url)

	local payload = HTTP:JSONEncode(getInitAnnotations(playerData, playerId))
	local authorization = encode(payload, secretKey)

	local res
	local success, err = pcall(function()
		res = HTTP:RequestAsync({
			Url = url,
			Method = "POST",
			Headers = {
				["Authorization"] = authorization,
				["Content-Type"] = "application/json",
			},
			Body = payload,
		})
	end)

	if not success then
		logger:d("Failed Init Call. error: " .. err)
		return {
			statusCode = http_api.EGAHTTPApiResponse.UnknownResponseCode,
			body = nil,
		}
	end
	logger:d("init request content: " .. res.Body)

	local requestResponseEnum = processRequestResponse(res, "Init")

	-- if not 200 result
	if requestResponseEnum ~= http_api.EGAHTTPApiResponse.Ok and requestResponseEnum ~= http_api.EGAHTTPApiResponse.Created and requestResponseEnum ~= http_api.EGAHTTPApiResponse.BadRequest then
		logger:d("Failed Init Call. URL: " .. url .. ", JSONString: " .. payload .. ", Authorization: " .. authorization)
		return {
			statusCode = requestResponseEnum,
			body = nil,
		}
	end

	--Response
	local responseBody
	success = ypcall(function()
		responseBody = HTTP:JSONDecode(res.Body)
	end)

	if not success then
		logger:d("Failed Init Call. Json decoding failed: " .. err)
		return {
			statusCode = http_api.EGAHTTPApiResponse.JsonDecodeFailed,
			body = nil,
		}
	end

	-- print reason if bad request
	if requestResponseEnum == http_api.EGAHTTPApiResponse.BadRequest then
		logger:d("Failed Init Call. Bad request. Response: " .. res.Body)
		return {
			statusCode = requestResponseEnum,
			body = nil,
		}
	end

	-- validate Init call values
	local validatedInitValues = validation:validateAndCleanInitRequestResponse(responseBody, requestResponseEnum == http_api.EGAHTTPApiResponse.Created)

	if not validatedInitValues then
		return {
			statusCode = http_api.EGAHTTPApiResponse.BadResponse,
			body = nil,
		}
	end

	-- all ok
	return {
		statusCode = requestResponseEnum,
		body = responseBody,
	}
end

function http_api:sendEventsInArray(gameKey, secretKey, eventArray)
	if not eventArray or #eventArray == 0 then
		logger:d("sendEventsInArray called with missing eventArray")
		return
	end

	-- Generate URL
	local url = baseUrl .. "/" .. gameKey .. "/" .. self.eventsUrlPath
	if RunService:IsStudio() then
		url = baseUrl .. "/5c6bcb5402204249437fb5a7a80a4959/" .. self.eventsUrlPath
	end

	logger:d("Sending 'events' URL: " .. url)

	-- make JSON string from data
	local payload = HTTP:JSONEncode(eventArray)
	local authorization = encode(payload, secretKey)

	local res
	local success, err = pcall(function()
		res = HTTP:RequestAsync({
			Url = url,
			Method = "POST",
			Headers = {
				["Authorization"] = authorization,
				["Content-Type"] = "application/json",
			},
			Body = payload,
		})
	end)

	if not success then
		logger:d("Failed Events Call. error: " .. err)
		return {
			statusCode = http_api.EGAHTTPApiResponse.UnknownResponseCode,
			body = nil,
		}
	end


	logger:d("body: " .. res.Body)
	local requestResponseEnum = processRequestResponse(res, "Events")

	-- if not 200 result
	if requestResponseEnum ~= http_api.EGAHTTPApiResponse.Ok and requestResponseEnum ~= http_api.EGAHTTPApiResponse.Created and requestResponseEnum ~= http_api.EGAHTTPApiResponse.BadRequest then
		logger:d("Failed Events Call. URL: " .. url .. ", JSONString: " .. payload .. ", Authorization: " .. authorization)
		return {
			statusCode = requestResponseEnum,
			body = nil,
		}
	end

	local responseBody
	ypcall(function()
		responseBody = HTTP:JSONDecode(res.Body)
	end)

	if not responseBody then
		logger:d("Failed Events Call. Json decoding failed")
		return {
			statusCode = http_api.EGAHTTPApiResponse.JsonDecodeFailed,
			body = nil,
		}
	end

	-- print reason if bad request
	if requestResponseEnum == http_api.EGAHTTPApiResponse.BadRequest then
		logger:d("Failed Events Call. Bad request. Response: " .. res.Body)
		return {
			statusCode = requestResponseEnum,
			body = nil,
		}
	end

	-- all ok
	return {
		statusCode = http_api.EGAHTTPApiResponse.Ok,
		body = responseBody,
	}
end

return http_api
