require "json"
local m_ltn12 = require "ltn12"
local m_mime = require "mime"

module(..., package.seeall)

local m_elasticSearch = {}
--[[
	Con este valor ajustamos el valor máximo que soporta ES a retornar en una búsqueda/paginado.
]]--
m_elasticSearch.MAX_ALLOWED_ROWS_IN_ES = 5000
--[[
	Con este valor ajustamos el valor máximo que soporta ES a retornar en una búsqueda/paginado desde la API.
]]--
m_elasticSearch.MAX_ALLOWED_ROWS_IN_ES_FROM_API = 50000

--[[
	 Tope máximo de registros permitidos por ES.
--]]
m_elasticSearch.GetMaxAllowedRowsInES = function(self, fromAPI)
	return fromAPI and self.MAX_ALLOWED_ROWS_IN_ES_FROM_API or self.MAX_ALLOWED_ROWS_IN_ES
end

--[[
	 Funcion encargada de validar el tope maximo de registros a retornar por ES.
	 Si el request esta solicitando mas registros de los soportados, los mismos se topean al maximo permitido.
	 En si se ubica en la ultima pagina soportada.
--]]
m_elasticSearch.MakeElasticSearchPaged = function (self, query, fromAPI)
	if type(query) ~= "table" then
		return
	end
	if (not query.from or not query.size) then
		-- si no hay from o size no va a dar error.
		local encodedValues = json.encode(query)
		--LogInfo("[HELPERS.elasticsearch:MakeElasticSearchPaged] 1--> Query for elasticsearch: %s.", encodedValues)
		return encodedValues, query
	end
	local MAX_ALLOWED_ROWS_IN_ES = self:GetMaxAllowedRowsInES(fromAPI) -- Tope máximo de registros permitidos por ES.
	if (query.from + query.size) <= MAX_ALLOWED_ROWS_IN_ES then
		-- 1 - cuando se han solicitado registros dentro del limite soportado.
		local encodedValues = json.encode(query)
		--LogInfo("[HELPERS.elasticsearch:MakeElasticSearchPaged] 2--> Query for elasticsearch: %s.", encodedValues)
		return encodedValues, query
	end

	-- 2 - cuando ha sobrepasado el limite soportado.
	LogInfo("[HELPERS.elasticsearch:MakeElasticSearchPaged] You have reached the maximum number of rows allowed by ES. MAX_ALLOWED_ROWS_IN_ES: %d. From: %s, To %s.", MAX_ALLOWED_ROWS_IN_ES, query.from, query.size)

	if query.size > MAX_ALLOWED_ROWS_IN_ES then
		-- 3 - si el tamaño de la pagaina es mayor a lo soportado (ej: solicito desde el registro 0 a un numero mayor al soportado)
		query.from = 0
		query.size = MAX_ALLOWED_ROWS_IN_ES
		local encodedValues = json.encode(query)
		LogInfo("[HELPERS.elasticsearch:MakeElasticSearchPaged] - Query for elasticsearch: %s. New paged values. From: %s, To %s.", encodedValues, query.from, query.size)
		return encodedValues, query
	end

	-- 4 - aca nos quedamos con la ultima pagina soportada.
	local beforePage = MAX_ALLOWED_ROWS_IN_ES - query.size
	beforePage = beforePage >= 0 and beforePage or 0
	query.from = beforePage

	-- 5 - retornamos el query encodeado.
	local encodedValues = json.encode(query)
	LogInfo("[HELPERS.elasticsearch:MakeElasticSearchPaged] + Query for elasticsearch: %s. New paged values. From: %s, To %s.", encodedValues, query.from, query.size)
	return encodedValues, query
end

--[[
--]]
m_elasticSearch.PitElasticSearch = function (self, vccId, connectionConfig)
	--LogDebug("[HELPERS.elasticsearch:PitElasticSearch] Get PIT id from elasticsearch. vccId: %s, connectionConfig: %s", vccId, connectionConfig)
	-- TODO: Implementar !! mover el codigo del repo "agent" shared/apps/utils.lua
end

--[[
--]]
m_elasticSearch.CountElasticSearch = function (self, query, vccId, connectionConfig)
	--LogDebug("[HELPERS.elasticsearch:PitElasticSearch] CountElasticSearch - elasticsearch. vccId: %s, connectionConfig: %s", vccId, connectionConfig)
	-- TODO: Implementar !! mover el codigo del repo "agent" shared/apps/utils.lua
end

--[[
--]]
m_elasticSearch.enabledAuthorization = true
m_elasticSearch.CanMakeAuthorization = function (self, userId, userPwd)
	-- TODO: Lo dejo por las dudas, por si tenemos que implementar alguna validacion, tipo se habilita o inhabilitan las autorizaciones por tal o cual cosa.
	if not self.enabledAuthorization then
		LogWarning("[HELPERS.elasticsearch:CanMakeAuthorization] --> Elastic search authentication is Disabled !!!")
	end
	return self.enabledAuthorization
end

--[[
--]]
m_elasticSearch.cachedUsersAuthorization = {}

--[[
--]]
m_elasticSearch.MakeAuthorization = function(self, userId, userPwd, userToken)
	if not self:CanMakeAuthorization(userId, userPwd) then
		LogInfo("[HELPERS.elasticsearch:MakeAuthorization] Authorization generation is disabled.")
		return
	end
	userId = userId or os.getenv("ELASTICSEARCH_CREDENTIALS_USER_ID")
	userPwd = userPwd or os.getenv("ELASTICSEARCH_CREDENTIALS_USER_PWD")
	if not userId or not userPwd then
		LogInfo("[HELPERS.elasticsearch:MakeAuthorization] --> without user or pwd !!")
		return
	end
	if not self.cachedUsersAuthorization[userId] then
		--LogDebug("[HELPERS.elasticsearch:MakeAuthorization] 1--> Generate - authorization for userId: '%s', userPwd '%s', param userToken: '%s'.", userId, userPwd, userToken)
		userToken = userToken or m_mime.b64(userId .. ":" .. userPwd)
		self.cachedUsersAuthorization[userId] = {
			userToken = userToken,
			header    = string.format("Basic %s", userToken)
		}
	--else
		--LogDebug("[HELPERS.elasticsearch:MakeAuthorization] 2--> Loading from cache - authorization for userId: '%s', userPwd '%s'.", userId, userPwd)
	end
	--LogDebug("[HELPERS.elasticsearch:MakeAuthorization] 3--> Authorization for userId: '%s', info '%s'.", userId, json.encode(self.cachedUsersAuthorization[userId]))
	return self.cachedUsersAuthorization[userId]
end

--[[
--]]
m_elasticSearch.MakeRequestInfo = function(self, info)
	assert(type(info) == "table")
	local requestInfo = {
		host = info.host or nil,
		port = info.port or nil,
		data = nil,
		encodedData = nil,
		headers = {
			["Authorization"]  = nil,
			["Content-Length"] = nil,
			["Content-Type"]   = info.contentType or "application/json"
		}
	}
	-- o tiene que venir info.query o info.encodedQuery
	if info.query then
		local encodedData, data = self:MakeElasticSearchPaged(info.query, info.fromAPI)
		if encodedData then
			requestInfo.data = data
			requestInfo.encodedData = encodedData
		end
	elseif info.encodedQuery then
		requestInfo.data = nil -- NO lo voy a desencodear NO es necesario
		requestInfo.encodedData = info.encodedQuery
	end
	if requestInfo.encodedData then
		requestInfo.source = m_ltn12.source.string(requestInfo.encodedData)
		requestInfo.headers["Content-Length"] = #tostring(requestInfo.encodedData)
	end
	local credentials = info.credentials or {}
	local authorization = self:MakeAuthorization(credentials.userId, credentials.userPwd, credentials.userToken)
	if authorization then
		requestInfo.headers["Authorization"] = authorization.header
	end
	--LogDebug("[HELPERS.elasticsearch:MakeRequestInfo] Generated RequestInfo: '%s'.", json.encode(requestInfo))
	return requestInfo
end

----------------------------------
return {
	GetElasticSearchInstance = function()
		return m_elasticSearch
	end,
	CountElasticSearch = function(self, query, vccId, connectionConfig)
		return m_elasticSearch:CountElasticSearch(query, vccId, connectionConfig)
	end,
	GetMaxAllowedRowsInES = function(self, fromAPI)
		return m_elasticSearch:GetMaxAllowedRowsInES(fromAPI)
	end,
	MakeAuthorization = function(self, userId, userPwd, userToken)
		return m_elasticSearch:MakeAuthorization(userId, userPwd, userToken)
	end,
	MakeElasticSearchPaged = function(self, query, fromAPI)
		return m_elasticSearch:MakeElasticSearchPaged(query, fromAPI)
	end,
	MakeRequestInfo = function(self, info)
		return m_elasticSearch:MakeRequestInfo(info)
	end,
	PitElasticSearch = function(self, vccId, connectionConfig)
		return m_elasticSearch:PitElasticSearch(vccId, connectionConfig)
	end
}
