local Synchronize = require "Synchronize"
local http = Synchronize("luasec_mutex", function()
	return require "socket.http"
end)

local json = require "cjson"
require "uuid"

local HTTP_DEFAULT_TIMEOUT_SEC = http.TIMEOUT

module(..., package.seeall)

-- *******************************************************************************
-- API REQUEST (GET, POST)
-- *******************************************************************************

local function SetGetTimeout (options)
	local timeoutSec = HTTP_DEFAULT_TIMEOUT_SEC
	if options then
		timeoutSec = tonumber(options.timeoutSec)
		if type(timeoutSec) ~= "number" then
			timeoutSec = HTTP_DEFAULT_TIMEOUT_SEC
		end
	end
	http.TIMEOUT = timeoutSec
	--LogDebug("[RASA.REQUEST:SetGetTimeout] http.TIMEOUT %s", http.TIMEOUT)
	return timeoutSec
end

local function MakeBasicHeader (additionalHeaders)
	local headers = {}
	for name, value in pairs(additionalHeaders or {}) do
		headers[name] = value
	end
	return headers
end

local function MakeContentTypeHeader (body, contentType, additionalHeaders)
	body = body or {}
	local headers = MakeBasicHeader(additionalHeaders)
	local encodedBody
	if contentType then
		headers["Content-Type"] = contentType
		encodedBody = body
	else
		if type(body) == "table" then
			headers["Content-Type"] = "application/json"
			encodedBody = json.encode(body)
		else
			headers["Content-Type"] = "text/plain"
			encodedBody = body
		end
	end
	headers["Content-Length"] = #encodedBody
	return headers, encodedBody
end

function Get (uri, headers, options)
	assert(uri)
	local startTime = os.time()

	headers = headers or {}
	local timeoutSec = SetGetTimeout(options)

	LogInfo("[RASA.REQUEST:Get] GET URI: '%s', headers: '%s', timeout: '%s' seconds.", uri, json.encode(headers), timeoutSec)

	local responseBody = {}
	local result, code, header = http.request{
		url = uri,
		method = "GET",
		headers = headers,
		sink = ltn12.sink.table(responseBody)
	}
	local response = table.concat(responseBody, "")
	local endTime = os.time()
	local elapsedTimeSec = endTime - startTime

	if code == 200 then
		LogInfo("[RASA.REQUEST:Get] GET Success. ELAPSED Time '%s' seconds, RESULT: '%s', CODE: '%s', HEADER: '%s'.", elapsedTimeSec, result, code, json.encode(header))
		--LogDebug("[RASA.REQUEST:Get] GET Success. RESPONSE: '%s'.", response)
		return true, response
	end
	LogError("[RASA.REQUEST:Get] GET Error. ELAPSED Time '%s' seconds, RESULT: '%s', CODE: '%s', HEADER: '%s', RESPONSE: '%s'.", elapsedTimeSec, result, code, json.encode(header), response)

	return false, response, code
end

local function ExecutePostRequest (RequestFn)
	local result, code, header, statusline
	local response = {}

	local startTime = os.time()

	result, code, header, statusline, response, uri = RequestFn()

	local endTime = os.time()
	local elapsedTimeSec = endTime - startTime

	-- OK o CREATED o ACCEPTED
	if code == 200 or code == 201 or code == 202 then
		LogInfo("[RASA.REQUEST:ExecutePostRequest] POST Success. ELAPSED Time '%s' seconds, CODE: '%s' URI: '%s'.", elapsedTimeSec, code, uri)
		local encodedResponse = table.concat(response)
		--LogDebug("[RASA.REQUEST:ExecutePostRequest] POST Success. Response: '%s'.", encodedResponse)
		return true, encodedResponse
	end

	LogError("[RASA.REQUEST:ExecutePostRequest] POST Error. ELAPSED Time '%s' seconds, RESULT: '%s', CODE: '%s', HEADER: '%s', RESPONSE: '%s'.", elapsedTimeSec, result, code, json.encode(header), response)

	local err = response
	return false, {code = code, header = header, statusline = statusline, response = err}
end

function Post (uri, body, contentType, additionalHeaders, options)
	body = body or {}
	local headers, encodedBody = MakeContentTypeHeader(body, contentType, additionalHeaders)

	local timeoutSec = SetGetTimeout(options)

	local function Request ()
		LogInfo("[RASA.REQUEST:Post] POST Request URI: '%s', headers: '%s', body: '%s', timeout: '%s' seconds.", uri, json.encode(headers), encodedBody, timeoutSec)

		local response = {}
		local result, code, header, statusline = http.request({
			url = uri,
			method = "POST",
			headers = headers,
			source = ltn12.source.string(encodedBody),
			--respuesta
			sink = ltn12.sink.table(response)
		})
		--LogDebug("[RASA.REQUEST:Post] POST request result: '%s', code: '%s' headers: '%s' , statusline: '%s', response: '%s'", result, code, json.encode(headers or {}), statusline, response)

		return result, code, header, statusline, response, uri
	end

	return ExecutePostRequest(Request)
end
