local json = require "cjson"
local request = require "luis.request"
local luis_models = require("luis.models"):GetModels()
local utils = require "luis.utils"
require "socket"

module(..., package.seeall)

local m_instanceOwner
function SetOwner (instanceOwner)
	m_instanceOwner = instanceOwner
end

local m_defaultApiVersion = "2022-10-01-preview"

local function MakeCompleteURI (uriBaseId, uri, apiVersion, ...)
	uriBaseId = uriBaseId or "APPS"
	return m_instanceOwner:MakeCompleteURI(m_defaultApiVersion, uriBaseId, uri, apiVersion, ...)
end

--[[
	Retorna los parametros:
		1) Si tiene paginado    : "?skip=0&take=2"
		2) Si no tiene paginado : ""
]]--
local function MakePagingParameters (params)
	local api = require "luis"
	return api.MakePagingParameters(params)
end

function LoadAllAppEntities (appId, versionId, paginated, vcc)
	assert(appId)
	assert(versionId)

	local resource_name, subscription_key_1 = GetLanguageStudioAccount(vcc)

	local uri = MakeCompleteURI("APPS", appId .."/:export?api-version=".. m_defaultApiVersion .. "&format=Conversation&stringIndexType=Utf16CodeUnit")
	uri = uri:gsub("resourcename", resource_name)

	LogInfo("[LUIS_APPS40:LoadAllAppEntities] uri: %s", uri)

	local additionalHeaders = {
		["Ocp-Apim-Subscription-Key"] = subscription_key_1
	}

	local success, response, headers = request.Post(uri, {}, "application/json; charset=UTF-8", additionalHeaders)
	if not success then
		LogError("[LUIS_APPS40:LoadAllAppEntities] LoadAllAppEntities Error: '%s'", json.encode(response))
		return false, response
	end

	-- luego obtengo el location
	local operationLocation = headers["operation-location"]

	LogInfo("[LUIS_ENTITIES_COMPOSITE:LoadAllAppEntities] operationLocation: %s", operationLocation)

	local success = false
	local response = {}
	local retry = 0
	local TOTAL_RETRIES = 10
	local TIMEOUT_SEC = 3

	while retry < TOTAL_RETRIES do
		retry = retry + 1
		
		success, response = request.Get(operationLocation, additionalHeaders)

		local encodedResponse = table.concat(response)
		local parsedResponse = json.decode(encodedResponse)

		if not success then
			LogError("[LUIS_ENTITIES_COMPOSITE:LoadAllAppEntities] Error loading all APP COMPOSITE ENTITIES on appId '%s' versionId: '%s'. Error: '%s'", appId, versionId, json.encode(response))
			return false, response
		end

		if parsedResponse.status == "succeeded" then

			success, response = request.Get(parsedResponse.resultUrl, additionalHeaders)
			
			encodedResponse = table.concat(response)			
			response = json.decode(encodedResponse)

			local entities = response.assets.entities

			for i, entity in ipairs(entities) do
				entity.id = entity.category
				entity.name = entity.category
				entity.typeId = entity.compositionSetting

				-- parseo las listas
				local list = {}

				if (entity.list ~= nil and type(entity.list) == "table") then

					if (entity.list.sublists ~= nil and type(entity.list.sublists) == "table") then

						local first_list = entity.list.sublists[1]
						local first_synonyms = first_list.synonyms[1]

						for _, c in pairs(first_synonyms.values) do
							table.insert(list, {
								language = first_synonyms.language,
								value = c
							})
						end
					end
				end
				
				entity.list = list

				-- parseo las expresiones regulares
				local regex = {}

				if (entity.regex ~= nil and type(entity.regex) == "table") then

					if (entity.regex.expressions ~= nil and type(entity.regex.expressions) == "table") then
						for _, c in pairs(entity.regex.expressions) do
							table.insert(regex, c)
						end
					end
				end
				
				entity.regex = regex

			end

			response = json.encode(entities)

			break
		else
			LogInfo("[LUIS_ENTITIES_COMPOSITE:LoadAllAppEntities] Waiting %d seconds.", TIMEOUT_SEC)
			socket.sleep(TIMEOUT_SEC)
		end
	end

	LogInfo("[LUIS_ENTITIES_COMPOSITE:LoadAllAppEntities] response: %s", json.encode(response))
	--local encodedEntities = table.concat(response)
	return true, response
end

function LoadApp (appId, versionId, paginated, vcc)
	assert(appId)
	assert(versionId)

	local resource_name, subscription_key_1 = GetLanguageStudioAccount(vcc)

	local uri = MakeCompleteURI("APPS", appId .."/:export?api-version=".. m_defaultApiVersion .. "&format=Conversation&stringIndexType=Utf16CodeUnit")
	uri = uri:gsub("resourcename", resource_name)

	LogInfo("[LUIS_APPS40:LoadApp] uri: %s", uri)

	local additionalHeaders = {
		["Ocp-Apim-Subscription-Key"] = subscription_key_1
	}

	local success, response, headers = request.Post(uri, {}, "application/json; charset=UTF-8", additionalHeaders)
	if not success then
		LogError("[LUIS_APPS40:LoadApp] LoadApp Error: '%s'", json.encode(response))
		return false, response
	end

	-- luego obtengo el location
	local operationLocation = headers["operation-location"]

	LogInfo("[LUIS_INTENTS:LoadApp] operationLocation: %s", operationLocation)

	-- luego consulto N veces ese location

	local success = false
	local response = {}
	local retry = 0
	local TOTAL_RETRIES = 10
	local TIMEOUT_SEC = 3

	while retry < TOTAL_RETRIES do
		retry = retry + 1
		
		success, response = request.Get(operationLocation, additionalHeaders)

		if not success then
			LogError("[LUIS_INTENTS:LoadApp] Error loading all APP COMPOSITE INTENTS on appId '%s' versionId: '%s'. Error: '%s'", appId, versionId, json.encode(response))
			return false, response
		end

		local encodedResponse = table.concat(response)
		local parsedResponse = json.decode(encodedResponse)

		if parsedResponse.status == "succeeded" then

			success, response = request.Get(parsedResponse.resultUrl, additionalHeaders)

			encodedResponse = table.concat(response)			
			response = json.decode(encodedResponse)

			break
		else
			LogInfo("[LUIS_INTENTS:LoadApp] Waiting %d seconds.", TIMEOUT_SEC)
			socket.sleep(TIMEOUT_SEC)
		end
	end

	LogInfo("[LUIS_INTENTS:LoadApp] response: %s", json.encode(response))

	return true, response
end

function GetLanguageStudioAccount (vcc)

	LogInfo(" ------> GetLanguageStudioAccount - vcc: %s", vcc)

	local vccAzureLanguageStudioAccounts, totalVCCAzureLanguageStudioAccounts = LoadVCCAzureLanguageStudioAccount(vcc)
	local azureLanguageStudio = {}
	if totalVCCAzureLanguageStudioAccounts > 0 then
		local firstKey = next(vccAzureLanguageStudioAccounts)
  		azureLanguageStudio = vccAzureLanguageStudioAccounts[firstKey]
	else
		azureLanguageStudio = {}
	end

    return azureLanguageStudio.resource_name, azureLanguageStudio.subscription_key_1
	
end

function LoadVCCAzureLanguageStudioAccount (vcc, vcc_id)
	
	if type(vcc) == "table" then
		vcc = vcc_id
	end

	local vccAzureLanguageStudioAccounts = luis_models.VCCs.GetAzureLanguageStudioAccountsMsCognitiveServices(vcc)

	local total = utils.GetRowCount(vccAzureLanguageStudioAccounts)
	return vccAzureLanguageStudioAccounts, total
end

function GetAppEntity (appId, entityId, versionId)
	assert(appId)
	assert(entityId)
	assert(versionId)

	local uri = MakeCompleteURI("APPS", "%s/versions/%s/entities/%s", appId, versionId, entityId)

	local success, response = request.Get(uri)
	if not success then
		LogError("[LUIS_ENTITIES_MACHINE_LEARNED.v3.0_PREVIEW:GetAppEntity] Error get APP MACHINE LEARNED ENTITIES '%s' on appId '%s' versionId: '%s' Error: '%s'", entityId, appId, versionId, json.encode(response))
		return false, response
	end

	LogInfo("[LUIS_ENTITIES_MACHINE_LEARNED.v3.0_PREVIEW:GetAppEntity] Success get APP MACHINE LEARNED ENTITIES '%s' on appId '%s' versionId: '%s'.", entityId, appId, versionId)

	local encodedEntity = table.concat(response)
	return true, encodedEntity
end

function CreateAppEntity (info, vcc)
	assert(type(info) == "table")
	assert(info.appId)
	assert(info.versionId)
	assert(vcc)

	local resource_name, subscription_key_1 = GetLanguageStudioAccount(vcc)

	local uri = MakeCompleteURI("APPS", info.appId .."?api-version=".. m_defaultApiVersion)	
	uri = uri:gsub("resourcename", resource_name)

	LogInfo("[LUIS_APPS40:Get] uri: %s", uri)

	local additionalHeaders = {
		["Ocp-Apim-Subscription-Key"] = subscription_key_1
	}

	local success, response = LoadApp(info.appId, info.versionId, info.paginated, vcc)
	if not success then
		LogError("[LUIS_APPS40:Get] Get App '%s' Error: '%s'", info.appId, json.encode(response))
		return false, response
	end

	LogInfo("[LUIS_APPS40:Get] Success Get App %s.", info.appId)

	local prebuilts = {}
	local regex = {
		expressions = {}
	}
	local list = {
		sublists = {}
	}

	if (type(info.regex) == "table" and #info.regex > 0) then
		for _, c in pairs(info.regex) do
			table.insert(regex.expressions, c)
		end
	end

	if (type(info.list) == "table" and #info.list > 0) then
		table.insert(list.sublists, {  -- se maneja actualmente una sola lista por entidad
			listKey = "list",
			synonyms = {}
		})

		table.insert(list.sublists[1].synonyms, {
			language = info.list[1].language,
			values = {}
		})

		for _, c in pairs(info.list) do
			table.insert(list.sublists[1].synonyms[1].values, c.value)
		end
	end

	for _, i in pairs(response.assets.entities) do

		LogInfo("[LUIS_APPS40:CreateAppEntity] i %s", json.encode(i))

		if i.category == info.name then
			if info.is_new == true then
				return false, {code = 400, reason = "There is already an attempt with the same name.", errType = "inconcert"}
			else
				i.prebuilts = #info.prebuilts > 0 and info.prebuilts or nil
				i.regex = #info.regex > 0 and regex or nil
				i.list = #info.list > 0 and list or nil
			end
		end

	end
	
	if info.is_new == true then
		table.insert(response.assets.entities, {
			category = info.name,
			prebuilts = #info.prebuilts > 0 and info.prebuilts or nil,
			regex = #info.regex > 0 and regex or nil,
			list = #info.list > 0 and list or nil
		})
	end

	local appId = info.appId .. "/"
	local uri_post = MakeCompleteURI("APPS", appId ..":import?api-version=".. m_defaultApiVersion)
	uri_post = uri_post:gsub("resourcename", resource_name)
	
	local success, response, headers = request.Post(uri_post, json.encode(response), "application/json; charset=UTF-8", additionalHeaders)
	if not success then
		LogError("[LUIS_APPS40:CreateAppEntity] CreateAppEntity Error: '%s'", json.encode(response))
		return false, response
	end

	return true, info.name

end


function RenameAppEntity (info)
	assert(info)
	assert(info.appId)
	assert(info.entityId)
	assert(info.versionId)
	assert(info.name)

	local uri = MakeCompleteURI("APPS", "%s/versions/%s/entities/%s", info.appId, info.versionId, info.entityId)

	local data = {
		-- Requerido
		name       = info.name,
		-- NO Requerido
		instanceOf = info.instanceOf
	}
	local success, response = request.Patch(uri, data)
	if not success then
		LogError("[LUIS_ENTITIES_MACHINE_LEARNED.v3.0_PREVIEW:RenameAppEntity] Error renaming APP ENTITIES '%s' on appId '%s' versionId: '%s'. Response: '%s'", info.entityId, info.appId, info.versionId, json.encode(response))
		return false, response
	end

	LogInfo("[LUIS_ENTITIES_MACHINE_LEARNED.v3.0_PREVIEW:RenameAppEntity] Success renamed APP ENTITIES '%s' on appId '%s' versionId: '%s'.", info.entityId, info.appId, info.versionId)

	return true, response
end


function DeleteAppEntity (appId, entityId, versionId, vcc)
	assert(appId)
	assert(entityId)
	assert(versionId)
	assert(vcc)

	LogInfo("[LUIS_APPS40:DeleteAppEntity] entityId: %s", entityId)

	local resource_name, subscription_key_1 = GetLanguageStudioAccount(vcc)

	local uri = MakeCompleteURI("APPS", appId .."?api-version=".. m_defaultApiVersion)	
	uri = uri:gsub("resourcename", resource_name)

	LogInfo("[LUIS_APPS40:Get] uri: %s", uri)

	local additionalHeaders = {
		["Ocp-Apim-Subscription-Key"] = subscription_key_1
	}

	local success, response = LoadApp(appId, versionId, nil, vcc)
	if not success then
		LogError("[LUIS_APPS40:Get] Get App '%s' Error: '%s'", appId, json.encode(response))
		return false, response
	end

	LogInfo("[LUIS_APPS40:Get] Success Get App %s.", appId)
	LogInfo("[LUIS_APPS40:Get] response %s", json.encode(response))

	--local encodedApp = json.decode(response)
	local toRemove = {}

	for index, i in pairs(response.assets.entities) do
		LogInfo("[LUIS_APPS40:DeleteAppEntity] i %s", json.encode(i))
	
		if i.category == entityId then
			table.insert(toRemove, index) -- Agregar el índice a la lista de elementos a eliminar
		end
	end
	
	for i = #toRemove, 1, -1 do
		table.remove(response.assets.entities, toRemove[i])
	end

	local appId = appId .. "/"
	local uri_post = MakeCompleteURI("APPS", appId ..":import?api-version=".. m_defaultApiVersion)
	uri_post = uri_post:gsub("resourcename", resource_name)
	
	local success, response, headers = request.Post(uri_post, json.encode(response), "application/json; charset=UTF-8", additionalHeaders)

	LogError("[LUIS_APPS40:DeleteAppEntity] DeleteAppEntity success: '%s'", success)
	LogError("[LUIS_APPS40:DeleteAppEntity] DeleteAppEntity Error: '%s'", json.encode(response))

	if not success then		
		return false, response
	end

	return true, {code = 200, code = "Success", message = "Operation Successful"}
end


function CreateAppEntityChild (info)
	assert(type(info) == "table")
	assert(info.appId)
	assert(info.entityId)
	assert(info.versionId)

	local uri = MakeCompleteURI("APPS", "%s/versions/%s/entities/%s/children", info.appId, info.versionId, info.entityId)

	local data = {
		name = info.name,
		instanceOf = info.instanceOf,
		children = info.children
	}

	local success, response = request.Post(uri, data)
	if not success then
		LogError("[LUIS_ENTITIES_MACHINE_LEARNED.v3.0_PREVIEW:CreateAppEntityChild] Error creating APP MACHINE LEARNED ENTITY CHILD. Parent Entity '%s' on appId '%s' versionId: '%s'. Response: '%s'", info.entityId, info.appId, info.versionId, json.encode(response))
		return false, response
	end

	local childEntityId = table.concat(response)
	LogInfo("[LUIS_ENTITIES_MACHINE_LEARNED.v3.0_PREVIEW:CreateAppEntityChild] Success created APP MACHINE LEARNED ENTITY CHILD '%s'. Parent Entity '%s' on appId '%s', entityId: '%s' versionId: '%s'.", childEntityId, info.entityId, info.appId, entityId, info.versionId)

	return true, childEntityId
end
