local m_helpers = require "helpers"
local m_repository = require "interactionmanip.repository"

local _M = {}

function _M.require_module (interaction)
	assert(type(interaction) == "table")

	m_repository:Init()

	if interaction.type == "CALL" then
		return require "interactionmanip.call"

	elseif interaction.type == "CHAT" then
		return require "interactionmanip.chat"

	elseif interaction.type == "FACEBOOK" then
		return require "interactionmanip.facebook"

	elseif interaction.type == "INSTAGRAM" then
		return require "interactionmanip.instagram"

	elseif interaction.type == "YOUTUBE" then
		return require "interactionmanip.youtube"

	elseif interaction.type == "APPSTORE" then
		return require "interactionmanip.appstore"

	elseif interaction.type == "GOOGLEPLAY" then
		return require "interactionmanip.googleplay"

	elseif interaction.type == "RCS" then
		return require "interactionmanip.rcs"

	elseif interaction.type == "MAIL" then
		return require "interactionmanip.mail"

	elseif interaction.type == "TWITTER" then
		return require "interactionmanip.twitter"

	elseif interaction.type == "VOICEMAIL" then
		return require "interactionmanip.voicemail"

	elseif interaction.type == "WEBCONTACT" then
		return require "interactionmanip.webcontact"

	elseif interaction.type == "SMS" then
		return require "interactionmanip.sms"

	elseif interaction.type == "WHATSAPP" then
		return require "interactionmanip.whatsapp"
	end

	assert(false, "Unrecognized interaction.type: '" .. tostring(interaction.type) .. "'")
end

function _M.updateReaders (interaction)
	-- already updated
	--if interaction.readers_v2 then
	--	return interaction
	--end

	local module = _M.require_module(interaction)
	if not module then return interaction end

	-- provide a default, for new interactions
	interaction.readers_v2 = interaction.readers_v2 or {}

	-- nothing to do with no events
	if _M.getAllEventsCount(interaction) == 0 then
		return interaction
	end

	-- recorrer la tabla 'readers' si existe, para migrar la data vieja
	if interaction.readers then
		--LogDebug("Updating readers in interaction '%s'", interaction.id)

		local readers = interaction.readers

		-- juntar en 'map' los índices de todas los eventos ya leidos
		local map = {}
		-- inicialmente, marco todo como UNREAD
		for i = 0, _M.getAllEventsCount(interaction) - 1 do
			map[i] = true
		end

		for agent, read_marks in pairs(readers) do
			-- procesamos las claves viejas
			for _, idx in ipairs(read_marks) do
				map[idx] = false
			end
		end

		local isProcesssFlow = _M.isProcessFlow(interaction)

		-- ahora marcamos todos los eventos cuyo creador sea agente como leidos
		for idx, event in ipairs(interaction.events) do
			if isProcesssFlow or event.creator == "agent" or event.creator == "automatic" or event.creator == "bot" then
				map[idx] = false
			end
		end

		local result = {}

		-- filtramos map y nos quedamos con lo que este unread
		for idx, unread in pairs(map) do
			if unread then
				result["I"..idx] = "U"
			end
		end

		interaction.readers = nil
		interaction.readers_v2 = result
	end

	return interaction
end

function _M.updateLabels (interaction)
	local module = _M.require_module(interaction)
	if not module then return interaction end

	-- recorrer la tabla 'labels' si existe, para migrar la data vieja
	if interaction.labels then
		--LogDebug("[INTERACTIONMAINIP] 1 Updating labels in interaction '%s', old labels: %s", interaction.id, json.encode(interaction.labels))
		for key, label in pairs(interaction.labels) do
			if not label.name then
				label.name = label.description or key
			end
		end
	end

	return interaction
end

function _M.isProcessFlow (interaction)
	return interaction.state == "CREATED" and interaction.processFlow and true or false
end

function _M.getFirstTransferringInfo (interaction)
	local transferInfo

	for _, step in ipairs(interaction.steps or {}) do
		--LogDebug("[INTERACTIONMAINIP.common.getFirstTransferringInfo] A - %d interaction: '%s',  transferInfo '%s'", _, interaction.id, json.encode(step or {}))

		if step.action == "TRANSFERRING" and step.origin_data then
			--LogDebug("[INTERACTIONMAINIP.common.getFirstTransferringInfo] B - %d interaction: '%s',  transferInfo '%s'", _, interaction.id, json.encode(step or {}))
			--[[
			SAMPLE
				origin_data:
					{ type: "campaign", from_campaign: {id: "camp1@inconcert", name: "CAMP1"}, from_account: {id: "account1", name: "ACCOUNT1"} };

					{ type: "attention_level", from_campaign: {id: "camp1@inconcert", name: "CAMP1"}, from_account: {id: "MAIL_cany.prueba@gmail.com", name: "ZZZZZ1"}, from_attention_level: { id: "8C26D184454621B8E01FD0986EF8B7F3", name: "ATTENTION LEVEL EXISTENTEEEEEEEEEEE"}};

				target_data: 
					{ type: "campaign", to_campaign: {id: "camp1@inconcert", name: "CAMP1"}, to_account: {id: "account1", name: "ACCOUNT1"} };

					{ type: "attention_level", to_campaign: {id: "camp1@inconcert", name: "CAMP1"}, to_account: {id: "MAIL_cany.prueba@gmail.com", name: "ZZZZZ1"}, to_attention_level: { id: "8C26D184454621B8E01FD0986EF8B7F3", name: "ATTENTION LEVEL EXISTENTEEEEEEEEEEE"}};
			--]]
			transferInfo = {
				message     = step.message or "",
				origin_data = step.origin_data,
				step        = step.action,
				target_data = step.to,
				timestamp   = step.timestamp,
				transferrer = step.transferrer or ""
			}
			break
		end
	end
	--LogDebug("[INTERACTIONMAINIP.common.getFirstTransferringInfo] C - interaction: '%s',  transferInfo '%s'", interaction.id, json.encode(transferInfo or {}))

	return transferInfo
end

function _M.getNextStep (interaction, params)
	--required params
	local action = params.action
	--optional params
	local index = params.index or 1
	local middle = params.middle or nil
	--1. iterate steps
	local steps = interaction.steps or {}
	for i = index, #steps do
		local step = steps[ i ]
		if step.action == action then
			--1.1 found
			return i, step
		elseif middle and not middle[step.action] then
			--1.2 invalid middle step: break
			return
		end
	end
end

function _M.isStepClosed (step)
	return step.action == "CANCELED" or step.action == "FINISHED" or step.action == "ABANDONED"
end

function _M.isTransferred (interaction)
	local steps = interaction.steps or {}
	local steps_count = #steps

	for current_index in ipairs( steps ) do
		local i = (steps_count + 1 ) - current_index
		local step = steps[ i ]
		local beforeStep = i > 1 and steps[ i - 1 ] or nil
		local nextStep = i < steps_count and steps[ i + 1 ] or nil

		if (step.action == "TRANSFERRING" or step.action == "CREATED") and step.origin_data then
			--[[
			SAMPLE

			origin_data:
				{ type: "campaign", from_campaign: {id: "camp1@inconcert", name: "CAMP1"}, from_account: {id: "account1", name: "ACCOUNT1"} };

				{ type: "attention_level", from_campaign: {id: "camp1@inconcert", name: "CAMP1"}, from_account: {id: "MAIL_cany.prueba@gmail.com", name: "ZZZZZ1"}, from_attention_level: { id: "8C26D184454621B8E01FD0986EF8B7F3", name: "ATTENTION LEVEL EXISTENTEEEEEEEEEEE"}};

			target_data: 
				{ type: "campaign", to_campaign: {id: "camp1@inconcert", name: "CAMP1"}, to_account: {id: "account1", name: "ACCOUNT1"} };

				{ type: "attention_level", to_campaign: {id: "camp1@inconcert", name: "CAMP1"}, to_account: {id: "MAIL_cany.prueba@gmail.com", name: "ZZZZZ1"}, to_attention_level: { id: "8C26D184454621B8E01FD0986EF8B7F3", name: "ATTENTION LEVEL EXISTENTEEEEEEEEEEE"}};
			--]]
			--check if transfer failed and was reverted
			local failed = false
			if step.action == "TRANSFERRING" then
				local failIndex, failStep = _M.getNextStep(interaction, {
					index = i,
					action = "TRANSFER FAILED",
					middle = { ["TRANSFER LOCKED"] = true, ["TRANSFER TAKEN"] = true }
				})
				if failStep then
					--failed: check if was reverted
					local revertStep = failIndex < steps_count and steps[ failIndex + 1 ] or nil
					failed = revertStep and not _M.isStepClosed(revertStep) and revertStep.action ~= "WRAPUP"
				end
			end
			if not failed then
				--LogDebug("[INTERACTIONMAINIP.common.isTransferred] 1---->  %d - interaction: '%s' -  step '%s'", i, interaction.id, json.encode(step or {}))
				interaction.transferInfo = {
					message     = step.message or "",
					origin_data = step.origin_data,
					step        = step.action,
					target_data = step.to,
					timestamp   = step.timestamp,
					transferrer = step.transferrer or ""
				}
				break
			end
		elseif step.action == "TRANSFER LOCKED" and beforeStep and beforeStep.action ~= "TRANSFERRING" and  beforeStep.transferring then
			if nextStep and nextStep.action == "TRANSFER TAKEN" then
				step = nextStep
				nextStep = i < steps_count - 1 and steps[ i + 2 ] or nil
			end
			--check if transfer failed and was reverted
			local failed = step.failReason and nextStep and not _M.isStepClosed(nextStep) and nextStep.action ~= "WRAPUP"
			if not failed then
				--LogDebug("[INTERACTIONMAINIP.common.isTransferred] 2---->  %d - interaction: '%s' -  beforeStep '%s'", i, interaction.id, json.encode(beforeStep or {}))
				interaction.transferInfo = {
					message     = beforeStep.transferring.message or "",
					origin_data = beforeStep.transferring.origin_data,
					step        = beforeStep.transferring.action,
					target_data = beforeStep.transferring.to,
					timestamp   = beforeStep.transferring.timestamp,
					transferrer = beforeStep.transferring.transferrer or ""
				}
				break
			end
		end
	end
	--LogDebug("[INTERACTIONMAINIP.common.isTransferred] 3--> interaction: '%s',  interaction.transferInfo '%s'", interaction.id, json.encode(interaction.transferInfo or {}))

	return interaction
end

function _M.getFirstAssignedAgent (interaction)
	if (#(interaction.assignedAgents or {}) == 0) or interaction.scheduledDate then
		return
	end
	return interaction.assignedAgents[1]
end

function _M.getLastStepAgent (interaction)
	interaction = _M.getLastStep(interaction)
	local agent
	if interaction.__last_step then
		-- Los agentes asignados en los diferentes Steps quedan en el atributo "agent". Menos en el Step "TRANSFER QUEUED" que queda en el atributo "to".
		agent = interaction.__last_step.agent or interaction.__last_step.to
	end
	--LogDebug("[INTERACTIONMAINIP.common.getLastStepAgent] interaction: '%s',  agent '%s', last step: '%s'.", interaction.id, agent, json.encode(interaction.__last_step or {}))
	return agent
end

--[[
	Esta funcion retorna el ultimo agente que ha finalizado la interaccion.
	 Se utiliza como campo calculado para las interacciones que no han sido infladas del lado del agente.
--]]
function _M.getLastAgentEndsConversation (interaction)
	if not _M.isFinished(interaction) then
		return interaction
	end

	local last_agent
	local steps = interaction.steps or {}
	local steps_count = #steps
	local last_step = steps[ steps_count ]

	if last_step and last_step.action == "FINISHED" and last_step.agent then
		-- (1) - Busca en el ultimo STEP si es "finished" y obtiene el agente (solo para interacciones archivadas nuevas).
		last_agent = last_step.agent
	else
		-- (2) - Si es una interaccion vieja, el agente no se encontraba en el step, entonces lo busca en la lista de agentes que la han tomado (el ultimo)
		local agents_taking = interaction.analysisTimes and interaction.analysisTimes.agentTakingTimestamps
		if agents_taking then
			local last_agent_taking = agents_taking[ #agents_taking ]
			if last_agent_taking then
				last_agent = last_agent_taking.agentTaking
			end
		end
	end
	-- el atributo calculado
	interaction.__last_agent_ends_conversation = last_agent

	--LogDebug("[INTERACTIONMAINIP.common.getLastAgentEndsConversation] interaction: '%s',  interaction.__last_agent_ends_conversation '%s'", interaction.id, interaction.__last_agent_ends_conversation)

	return interaction
end

function _M.isTaken (interaction)
	return interaction.state == "TAKEN"
end

function _M.isWrappedUp (interaction)
	return interaction.state == "WRAPUP"
end

function _M.isFinished (interaction)
	return interaction.state == "FINISHED" or interaction.state == "CANCELED" or interaction.state == "SPAM"
end

function _M.isAbandoned (interaction)
	return interaction.state == "ABANDONED"
end

function _M.isQueued (interaction)
	return interaction.state == "QUEUED" or interaction.state == "TRANSFER QUEUED"
end

function _M.getConversationOwner (interaction)
	--LogDebug("[INTERACTIONMAINIP.common.getConversationOwner] interaction data: %s", json.encode(interaction))

	local owner
	if _M.isTaken(interaction) or _M.isWrappedUp(interaction) then
		owner = _M.getFirstAssignedAgent(interaction)

	elseif _M.isFinished(interaction) then
		_M.getLastAgentEndsConversation(interaction)
		owner = interaction.__last_agent_ends_conversation

	elseif _M.isAbandoned(interaction) then
		owner = _M.getLastStepAgent(interaction)

	elseif _M.isQueued(interaction) then
		owner = _M.getLastStepAgent(interaction)

	-- else
		-- LogInfo("[INTERACTIONMAINIP.common.getConversationOwner] interaction: '%s' in state: '%s', without owner.", interaction.id, interaction.state)
	end

	--LogDebug("[INTERACTIONMAINIP.common.getConversationOwner] interaction: '%s', owner '%s'", interaction.id, owner)

	return owner
end

function _M.getLastEventTimestamp (interaction)
	local lastEventTimestamp = interaction.lastEventTimestampStr or interaction.timestamp
	--LogDebug("[INTERACTIONMAINIP.common.getLastEventTimestamp] interaction: '%s', lastEventTimestamp '%s'", interaction.id, lastEventTimestamp)
	return lastEventTimestamp
end

--[[
	Esta funcion retorna el ultimo step de la interaccion.
	 Se utiliza como campo calculado para las interacciones que no han sido infladas del lado del agente.
--]]
function _M.getLastStep (interaction)
	local steps = interaction.steps or {}
	local steps_count = #steps
	local last_step = steps[ steps_count ]
	-- el atributo calculado
	interaction.__last_step = last_step

	--LogDebug("[INTERACTIONMAINIP.common.getLastStep] interaction: '%s',  interaction.__last_step '%s'", interaction.id, interaction.__last_step)

	return interaction
end

function _M.isAutoResponseEvent (interaction, index)
	if not interaction.events or not interaction.events[index] then
		return false
	end
	return (interaction.events[index].creator == "automatic")
end

function _M.getUnreadEventCount (interaction)
	local count = 0
	for _ in pairs(interaction.readers_v2 or {}) do
		count = count + 1
	end
	--LogDebug("[INTERACTIONMAINIP.getUnreadEventCount -> interaction: '%s', count %d, readers_v2: '%s'", interaction.id, count, json.encode(interaction.readers_v2))
	return count
end

function _M.getUnAnsweredContactEventCount (interaction)
	if _M.getAllEventsCount(interaction) == 0 or interaction.events[#interaction.events].creator ~= "contact" 
	or _M.isFinished(interaction) or _M.isAbandoned(interaction) then
		return 0
	end
	local count = 1
	for i = #interaction.events - 1 , 1, -1 do
		local value = interaction.events[i]
		if value.creator == "contact" then
			count = count + 1
		else
			break
		end
	end

	return count
end

-- Cuenta la cantidad de eventos existentes (No cuenta las autorespuestas)
function _M.getEventsCountWithoutAutoResponse (interaction)
	if not interaction.events then
		return 0
	end
	local count = 0
	for i = 1, #interaction.events do
		if not _M.isAutoResponseEvent(interaction, i) then
			count = count + 1
		end
	end
	return count
end

function _M.getAllEventsCount (interaction)
	if not interaction.events then
		return 0
	end
	return #interaction.events
end

function _M.getFirstResponseOfAgent (interaction)
	local firstResponse
	local events = interaction.events or {}

	--LogDebug("-------> [INTERACTIONMAINIP.common.getFirstResponseOfAgent] events: '%s'", json.encode(events))

	for _, event in ipairs(events) do
		if event.creator == "agent" then
			firstResponse = event
			break
		end
	end

	--LogDebug("[INTERACTIONMAINIP.common.getFirstResponseOfAgent] interaction: '%s', initiative: '%s' firstResponse '%s'", interaction.id, interaction.initiative, json.encode(firstResponse or {}))

	return firstResponse
end

function _M.getLastResponseOfAgent (interaction)
	local lastResponse
	local events = interaction.events or {}

	--LogDebug("-------> [INTERACTIONMAINIP.common.getLastResponseOfAgent] events: '%s'", json.encode(events))

	for i = #events, 1, -1 do
		local event = events[i]
		--LogDebug("event.creator: %s, event.agent: %s", event.creator, event.agent)
		if event.creator == "agent" then
			lastResponse = event
			break
		end
	end

	--LogDebug("[INTERACTIONMAINIP.common.getLastResponseOfAgent] interaction: '%s', initiative: '%s' lastResponse '%s'", interaction.id, interaction.initiative, json.encode(lastResponse or {}))

	return lastResponse
end

--Auxiliar to newInteractionEvent and newInteractionEvents
local function processInteractionEvent (interaction, event, sender, index)
	--LogDebug("[INTERACTIONMAINIP.common.processInteractionEvent] interaction: '%s' sender: '%s', event index: '%d'", interaction.id, sender, index)
	if sender == "contact" and not _M.isProcessFlow(interaction) then
		interaction.readers_v2["I"..index] = "U"
	end
	return interaction
end

---
-- Note: the event is assumed to be already present in 'interaction'
-- @tparam table interaction
-- @tparam table event
-- @tparam string sender (agent, contact, automatic)
--
function _M.newInteractionEvent (interaction, event, sender)
	-- Add the new event as unread, only if it is from a contact
	local all_events_count = _M.getAllEventsCount(interaction)
	if all_events_count == 0 then return interaction end

	if not interaction.readers_v2 then
		_M.updateReaders(interaction)
	end

	interaction = processInteractionEvent(interaction, event, sender, all_events_count - 1)
	interaction.properties.unread_event_count = _M.getUnreadEventCount(interaction)

	return interaction
end

---
-- Note: events assumed to be already present in 'interaction'
-- @tparam table interaction
-- @tparam array events
--
function _M.newInteractionEvents (interaction, events)
	-- Add the new event as unread, only if it is from a contact
	local all_events_count = _M.getAllEventsCount(interaction)
	if all_events_count == 0 then return interaction end
	local event_index = all_events_count - #events

	if not interaction.readers_v2 then
		_M.updateReaders(interaction)
	end

	for _, event in pairs(events) do
		interaction = processInteractionEvent(interaction, event.event, event.sender, event_index)
		event_index = event_index + 1
	end
	interaction.properties.unread_event_count = _M.getUnreadEventCount(interaction)

	return interaction
end

function _M.getSocialNetworkPostId (interaction)
	return interaction.interactionData and interaction.interactionData.postId or nil
end

function _M.getBatchId (interaction)
	return interaction.interactionData and interaction.interactionData.batchId or nil
end

--[[
	Esta función retorna el primer STEP en PENDING.
	El estado PENDING NO existe, pero nosotros retornamos el primer estado QUEUED o WAITING.

	La misma es utilizada desde la rocka de los reportes "reporter.redis_values".
--]]
function _M.loadFirstPendingStep (interaction)
	assert(interaction)

	if interaction.initiative == "OUTBOUND" then
		return
	end

	--LogDebug("[INTERACTIONMAINIP.common.loadFirstPendingStep] interaction: '%s',  ", json.encode(interaction))
	local steps = interaction.steps or {}
	for _, step in ipairs(steps) do
		if step.action == "WAITING" or step.action == "QUEUED" then
			interaction.__firstPendingStep = step
			break
		end
	end
	--LogDebug("[INTERACTIONMAINIP.common.loadFirstPendingStep] interaction: '%s',  interaction.__firstPendingStep '%s'", interaction.id, json.encode(interaction.__firstPendingStep or {}))

	return interaction
end

--[[
	Esta función retorna el estado acutal de la interacción (PENDING, PENDING_REQUEUED, IN_PROCESS, IN_PROCESS_WRAPUP)

		PENDING = Si la interacción nunca ha sido tomada por nigún agente.
		PENDING_REQUEUED = Si la misma ha sido tomada al menos una vez por algún agente, pero en este momento se encuentra encolada.
		IN_PROCESS = Si la misma la tiene tomada algún agente.
		IN_PROCESS_WRAPUP = Si la misma la tiene tomada algún agente y se encuentra en wrapup.

	La misma es utilizada desde la rocka de los reportes "reporter.redis_values".
--]]
function _M.loadStatusInteraction (interaction)
	assert(interaction)

	--LogDebug("[INTERACTIONMAINIP.common.loadStatusInteraction] interaction: '%s',  ", json.encode(interaction))

	local isQueued = false
	local isTaken = false
	local status

	local steps = interaction.steps or {}
	for i = #steps, 1, -1 do
		local step = steps[i]
		--LogDebug("step.action: %s", step.action)
		if step.action == "WAITING" or step.action == "QUEUED" then
			isQueued = true
			status = "PENDING"
		elseif step.action == "TAKEN" or step.action == "TRANSFER TAKEN" or step.action == "CONFERENCE TAKEN" then
			isTaken = true
			if isQueued then
				status = "PENDING_REQUEUED"
			else
				status = "IN_PROCESS"
			end
			break
		elseif step.action == "WRAPUP" then
			status = "IN_PROCESS_WRAPUP"
			break
		end
	end

	interaction.__statusInteraction = status

	--LogDebug(" --> Interaction: %s status: %s", interaction.id, status)

	--LogDebug("[INTERACTIONMAINIP.common.loadStatusInteraction] interaction: '%s',  interaction.__statusInteraction '%s'", interaction.id, interaction.__statusInteraction)

	return interaction
end

--[[
	Esta función retorna el primer Step en Taken
	La misma es utilizada desde la rocka de los reportes "reporter.redis_values".
--]]
function _M.loadFirstTakenStep (interaction)
	assert(interaction)

	--LogDebug("[INTERACTIONMAINIP.common.loadFirstTakenStep] interaction: '%s',  ", json.encode(interaction))
	local steps = interaction.steps or {}
	for _, step in ipairs(steps) do
		if step.action == "TAKEN" then
			interaction.__firstTakenStep = step
			break
		end
	end
	--LogDebug("[INTERACTIONMAINIP.common.loadFirstTakenStep] interaction: '%s',  interaction.__firstTakenStep '%s'", interaction.id, json.encode(interaction.__firstTakenStep or {}))

	return interaction
end

--[[
	Esta función retorna la fecha y hora en la cual se ha leído el primer evento (mail)
	La misma es utilizada desde la rocka de los reportes "reporter.redis_values".
--]]
function _M.loadStartAttention (interaction)
	assert(interaction)

	--LogDebug("[INTERACTIONMAINIP.common.loadStartAttention] interaction: '%s',  ", json.encode(interaction))
	if interaction.initiative == "OUTBOUND" then
		return
	end

	local startAttention
	local events = interaction.events or {}
	local firstEvent = events[1]

	if firstEvent then
		startAttention = firstEvent.attentionTimestamp
	end

	interaction.__startAttention = startAttention

	--LogDebug("[INTERACTIONMAINIP.common.loadStartAttention] interaction: '%s',  interaction.__startAttention '%s'", interaction.id, interaction.__startAttention)

	return interaction
end

--[[
	Esta función retorna el número de ticket
	La misma es utilizada desde la rocka de los reportes "reporter.redis_values".
--]]
function _M.loadTicket (interaction)
	assert(interaction)
	--LogDebug("[INTERACTIONMAINIP.common.loadTicket] interaction: '%s',  ", json.encode(interaction))
	interaction.__ticket = interaction.interactionData and interaction.interactionData.ticket
	--LogDebug("[INTERACTIONMAINIP.common.loadTicket] interaction: '%s',  interaction.__ticket '%s'", interaction.id, interaction.__ticket)
	return interaction
end

--[[
	Esta función retorna el primer agente que ha respondido en la interacción
	La misma es utilizada desde la rocka de los reportes "reporter.redis_values".
--]]
function _M.loadFirstAgentResponse (interaction)
	assert(interaction)

	--LogDebug("[INTERACTIONMAINIP.common.loadFirstAgentResponse] interaction: '%s',  ", json.encode(interaction))
	local firstAgentResponseEvent = _M.getFirstResponseOfAgent(interaction)
	if firstAgentResponseEvent then
		interaction.__firstAgentResponse = firstAgentResponseEvent.agent
	end
	--LogDebug("[INTERACTIONMAINIP.common.loadFirstAgentResponse] interaction: '%s',  interaction.__firstAgentResponse '%s'", interaction.id, interaction.__firstAgentResponse)

	return interaction
end

--[[
	Esta función retorna el último agente que ha respondido en la interacción
	La misma es utilizada desde la rocka de los reportes "reporter.redis_values".
--]]
function _M.loadLastAgentResponse (interaction)
	assert(interaction)

	--LogDebug("[INTERACTIONMAINIP.common.loadLastAgentResponse] interaction: '%s',  ", json.encode(interaction))
	local lastAgentResponseEvent = _M.getLastResponseOfAgent(interaction)
	if lastAgentResponseEvent then
		interaction.__lastAgentResponse = lastAgentResponseEvent.agent
	end
	--LogDebug("[INTERACTIONMAINIP.common.loadLastAgentResponse] interaction: '%s',  interaction.__lastAgentResponse '%s'", interaction.id, interaction.__lastAgentResponse)

	return interaction
end

--[[
	******************** FACEBOOK, INSTAGRAM, YOUTUBE, TWITTER ***********************************************

	Esta función retorna el SUB TIPO para las interacciones de (FACEBOOK, INSTAGRAM, TWITTER)
	La misma es utilizada desde la rocka de los reportes "reporter.redis_values".
--]]
function _M.loadSubType (interaction)
	assert(interaction)
--	LogDebug("[INTERACTIONMAINIP.common.loadSubType] interaction: '%s',  ", json.encode(interaction))
	interaction.__displaySubType = interaction.interactionData and interaction.interactionData.subtype or nil
--	LogDebug("[INTERACTIONMAINIP.common.loadSubType] interaction: '%s',  interaction.__displaySubType '%s'", interaction.id, interaction.__displaySubType)
end

--[[
	******************** FACEBOOK, INSTAGRAM ***********************************************

	Esta función retorna el PAGE para las interacciones de (FACEBOOK, INSTAGRAM)
	La misma es utilizada desde la rocka de los reportes "reporter.redis_values".
--]]
function _M.loadPage (interaction)
	assert(interaction)
	--LogDebug("[INTERACTIONMAINIP.common.loadPage] interaction: '%s',  ", json.encode(interaction))
	interaction.__displayPage = interaction.interactionData and interaction.interactionData.page or nil
	--LogDebug("[INTERACTIONMAINIP.common.loadPage] interaction: '%s',  interaction.__displayPage '%s'", interaction.id, interaction.__displayPage)
end


--[[
	Esta función obtiene/calcula y retorna los valores de "analysisTimes".
	Para realizar estos cálculos lo hace contra el SUMMARIZER, realizando un "dofile" al archivo "InteractionDetail.lua".
	Después de realizar el "dofile" el mismo nos deja expuesta la función "StartProcessInteractionDetail".
	Invocamos la misma pasándole como parámetros la interacción para que realice los cálculos pertinentes.

    Ej: para obtener el "AttentionTime"
--]]

function _M.loadAnalysisTimesProperties (myInteraction)
	assert(myInteraction)
	if myInteraction.state ~= "FINISHED" then
		--LogDebug("[INTERACTIONMAINIP.common.loadAnalysisTimesProperties] Unable to calculate analysis time properties. Interaction: '%s', state: %s", myInteraction.id, myInteraction.state)
		return myInteraction
	end
	myInteraction.properties = myInteraction.properties or {}
	--LogDebug("[INTERACTIONMAINIP.common.loadAnalysisTimesProperties] myInteraction: '%s',  ", json.encode(myInteraction))

	-- funcion StartProcessInteractionDetail existente en InteractionDetail.lua
	if not StartProcessInteractionDetail then
		--[[
			Realizo un dofile y le envío como parámetro la "interaction"
		]]--
		interaction = myInteraction dofile("/usr/share/inconcert/summarizer/detailScripts/InteractionDetail.lua")
	end

	local _, interactionInfo = StartProcessInteractionDetail(myInteraction)

	--LogDebug("[INTERACTIONMAINIP.common.loadAnalysisTimesProperties] ----> interactionInfo: %s", json.encode(interactionInfo or {}))

	myInteraction.properties.analysisTimes = {IRInfo = {}, ReporterInfo = {}}

	-- Esto y (*) se realiza para que no viaje toda la información hacia el lado del cliente (interacciones sin montar).
	if myInteraction.analysisTimes then
		myInteraction.properties.analysisTimes.IRInfo.AttentionTime = myInteraction.analysisTimes.attentionTime
	end

	-- (*)
	if interactionInfo and interactionInfo.detail then
		myInteraction.properties.analysisTimes.ReporterInfo.AttentionTime = interactionInfo.detail.AttentionTime or 0
	end

	--LogDebug("[INTERACTIONMAINIP.common.loadAnalysisTimesProperties] myInteraction: '%s',  myInteraction.analysisTimes '%s'", myInteraction.id, json.encode(myInteraction.analysisTimes))

	return myInteraction
end

--[[

]]--
function _M.calculateConversationTimeSec (interaction)
	--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTimeSec] Channel: '%s' has not implement function. Interaction: '%s'", interaction.type, interaction.id)
	return 0
end

--[[

]]--
function _M.calculateConversationTotalCharacters (interaction)
	--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharacters] Channel: '%s' has not implement function. Interaction: '%s'", interaction.type, interaction.id)
	return 0
end


--[[
	funcion invocada desde el API SDK.
]]--
function _M.getConversationTotals (interaction)
	assert(interaction)
	--LogDebug("[INTERACTIONMAINIP.common.getConversationTotals] 1 -----> Loading Totals of Interaction: '%s'", interaction.id)
	if not interaction.interactionData or not interaction.interactionData.totals then
		--LogDebug("[INTERACTIONMAINIP.common.getConversationTotals] 2 - NO totals: interaction'%s'", interaction.id)
		return false
	end
	--LogDebug("[INTERACTIONMAINIP.common.getConversationTotals] 3 - Interaction: '%s', totals: %s", interaction.id, json.encode(interaction.interactionData.totals))
	return interaction.interactionData.totals
end

--[[
	Funcion en comun para los canales de Facebook e Instagram
]]--
function _M.calculateConversationTotalCharactersFacebookInstagram (interaction)
	assert(interaction)
	--LogDebug("-------------------------------------------------")
	--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersFacebookInstagram] ENTRA interaction: '%s' type: %s.", interaction.id, interaction.type)
	--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersFacebookInstagram] interaction: '%s' ", m_helpers.table:JSON_Encode(interaction))
	--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersFacebookInstagram] interaction.events: '%s' ", m_helpers.table:JSON_Encode(interaction.events))

	local function MustCalculate (event)
		if not event.message then
			--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersFacebookInstagram] Not found message attribute.")
			return false
		end
		if type(event.message.message) == "string" then
			--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersFacebookInstagram] event.message.message is string.")
			return true, event.message.message
		end
		if type(event.message.text) == "string" then
			--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersFacebookInstagram] event.message.message.text is string.")
			return true, event.message.text
		end
		--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersFacebookInstagram] Only calculate 'text' messages.")
		return false
	end

	local totalCharacters = 0

	for i, event in ipairs(interaction.events or {}) do
		--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersFacebookInstagram] %d --> event: '%s'", i, m_helpers.table:JSON_Encode(event))
		local success, text = MustCalculate(event)
		if success then
			local length = #text
			totalCharacters = totalCharacters + length
			--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersFacebookInstagram] length: '%s' in text: %s.", length, text)
		end
	end

	--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersFacebookInstagram] SALE totalCharacters: '%s', interaction: '%s' ", totalCharacters, interaction.id)
	return totalCharacters
end

--[[
	Funcion en comun para los canales de: webchat, whatsapp, sms, etc
--]]
function _M.calculateConversationTotalCharactersCommonChats (messages, interaction, MustCalculateFn, SanitizeMessageFn)
	assert(interaction)
	--[[
		Remuevo los Enter y retorno de carro.
	]]--
	messages = messages or {}
	local SanitizeMessage = SanitizeMessageFn or function (text)
		return m_helpers.string:CleanCR(text or "")
	end

	local MustCalculate = MustCalculateFn or function (message)
		local msgtype = (message.msgtype or (message.message and message.message.msgtype) or ""):lower()
		if msgtype ~= "text" then
			--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersCommonChats] Only calculate 'text' messages. Current msgtype: '%s'", tostring(msgtype))
			return false
		end
		local text = message.text or (message.message and message.message.text)
		if type(text) ~= "string" then
			--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersCommonChats] Only calculate 'text' messages. Current text: '%s'", tostring(text))
			return false
		end
		return true, text
	end

	local totalCharacters = 0

	for i, message in ipairs(messages) do
		--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersCommonChats] %d --> message: '%s'", i, json.encode(message))
		local success, text = MustCalculate(message)
		if success then
			text = SanitizeMessage(text)
			local length = #text
			totalCharacters = totalCharacters + length
			--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersCommonChats] length: '%s' in text: %s.", length, text)
		end
	end

	--LogDebug("[INTERACTIONMAINIP.common.calculateConversationTotalCharactersCommonChats] SALE totalCharacters: '%s', interaction: '%s' ", totalCharacters, interaction.id)
	return totalCharacters
end

function _M.updateInteraction (interaction)
	assert(interaction)
	--LogDebug("[INTERACTIONMAINIP.common.updateInteraction] ------> ENTRA. interaction: '%s'.", interaction.type, interaction.id)
	interaction.properties = interaction.properties or {}

	-- Obtengo los analysistimes
	_M.loadAnalysisTimesProperties(interaction)

	local module = _M.require_module(interaction)
	if not module then return interaction end
	interaction = module.updateInteraction(interaction)

	_M.require_module(interaction)

	return interaction
end

function _M.GetRepository()
	return m_repository
end

return _M
