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

module(..., package.seeall)

local m_instanceOwner
function SetOwner (instanceOwner)
	m_instanceOwner = instanceOwner
end

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

local function GetBaseURI (id)
	id = id or "APPS"
	return m_instanceOwner:GetBaseURI(id)
end

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 GetLanguageStudioAccount (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

-- *******************************************************************************
-- API APPS
-- *******************************************************************************

local function ParseAppFormat(app) 

	if app ~= nil then

		local convertedJson = {
			description = app.metadata.description,
			isNew = false,
			tokenizerVersion = "",
			activeVersion = app.metadata.projectName,
			versionsCount = 0,
			createdDateTime = nil,
			culture = app.metadata.language,
			endpointHitsCount = 0,
			id = app.metadata.projectName,
			domain = "",
			name = app.metadata.projectName,
			usageScenario = "",
			__occName = "",
			__occDescription = "",
			api_version = m_defaultApiVersion,
			intentsCount = #app.assets.intents,
			intents = app.assets.intents,
			entitiesCount = #app.assets.entities,
			entities = app.assets.entities,
			utterancesCount = #app.assets.utterances,
			utterances = app.assets.utterances
		}

		return json.encode(convertedJson)
	else
		return app
	end

end

function Get (appId, vcc, without_parse) 

	assert(appId)
	assert(vcc)

	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'. Error: '%s'", appId, 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))

	if without_parse then
		return true, response
	else
		return true, ParseAppFormat(response)
	end
	
end

function Add (info, vcc) 

	assert(type(info) == "table")
	assert(type(info.name) == "string")
	assert(type(info.culture) == "string")
	assert(type(info.api_version) == "string")

	local resource_name, subscription_key_1 = GetLanguageStudioAccount(vcc)

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

	local additionalHeaders = {
		["Ocp-Apim-Subscription-Key"] = subscription_key_1
	}
	
	local data = {
		multilingual = true,
		projectName = info.name,
		description = info.description,
		projectKind = "Conversation",
		language = "en-us",
		settings = {
			confidenceThreshold = 0
		}
	}

	local success, response = request.Patch(uri, data, "application/merge-patch+json; charset=UTF-8", additionalHeaders)
	if not success then
		LogError("[LUIS_APPS40:Add] Adding App Error: '%s'", json.encode(response))
		return false, response
	end

	local decodedResponse = json.decode(response[1])

	local appId = decodedResponse.projectName

	LogInfo("[LUIS_APPS40:Add] Success added appId %s.", appId)

	return true, appId

end

function Rename (info, vcc)
	assert(type(info) == "table")
	assert(info.id)
	assert(vcc)

	local resource_name, subscription_key_1 = GetLanguageStudioAccount(vcc)

	local uri = MakeCompleteURI("APPS", info.id .."?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_get_app = Get(info.id, vcc, true)
	if not success then
		LogError("[LUIS_APPS40:Get] Get App '%s' Error: '%s'", info.id, json.encode(response_get_app))
		return false, response_get_app
	end

	LogInfo("[LUIS_APPS40:Get] Success Get App %s.", info.id)
	LogInfo("[LUIS_APPS40:Get] response_get_app %s", json.encode(response_get_app))

	response_get_app.metadata.description = info.name

	local appId = info.id .. "/"
	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_get_app), "application/json; charset=UTF-8", additionalHeaders)
	if not success then
		LogError("[LUIS_APPS40:Rename] Error: '%s'", json.encode(response))
		return false, response
	end

	return true, response_get_app
end

function Delete (appId, vcc)
	assert(type(appId) == "string")
	assert(type(vcc) == "string")

	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:Delete] uri: %s", uri)

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

	local success, response = request.Delete(uri, nil, nil, additionalHeaders)
		if not success then

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

		if response and tostring(response.code) == "404" and (response.response and response.response.error) then
			LogInfo("[LUIS_APPS40:Delete] Application '%s' does not exist in the Language Studio Database. Response: '%s'.", appId, json.encode(response))
			success = true
			response = {code = "Success", message = "Operation Successful"}
			return true, response
		end
		LogError("[LUIS_APPS40:Delete] Deleting App '%s' Error: '%s'", appId, json.encode(response))
		return false, response
	end

	LogInfo("[LUIS_APPS40:Delete] Success deleted app %s.", appId)

	--LogDebug("[LUIS_APPS40:Delete] response: '%s'", json.encode(response))
	return true, response
end

function LoadAppCultures (vcc, apiVersion)

	apiVersion = (#tostring(apiVersion) > 0 and tostring(apiVersion) ~= "undefined") and apiVersion or m_defaultApiVersion

	local resource_name, subscription_key_1 = GetLanguageStudioAccount(vcc)

	if not subscription_key_1 then
		return false, {code = 400, reason = "There is no Azure Language Studio subscription", errType = "inconcert"}
	end

	local uri = MakeCompleteURI("APPS", "global/languages?api-version=".. apiVersion .."&projectKind=Orchestration")
	
	uri = uri:gsub("resourcename", resource_name)

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

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

	local success, response = request.Get(uri, additionalHeaders)
	if not success then
		LogError("[LUIS_APPS_40:LoadAppCultures] Loading Application Cultures. Error: '%s'", json.encode(response))
		return false, response
	end

	LogInfo("[LUIS_APPS40:LoadAppCultures] Success Loaded Application Cultures.")

	local encodedCultures = table.concat(response)

	LogDebug("[LUIS_APPS40:LoadAppCultures] Loaded Application Cultures, data: '%s'", encodedCultures)

	local data = json.decode(encodedCultures)

	local result = {}
	for _, item in ipairs(data.value) do
		table.insert(result, { name = item.languageName, code = item.languageCode })
	end

	-- Convertir la tabla a JSON
	local resultJson = json.encode(result)

	return true, resultJson
end

function LoadAppCulturesForIntents (vcc, apiVersion)

	apiVersion = (#tostring(apiVersion) > 0 and tostring(apiVersion) ~= "undefined") and apiVersion or m_defaultApiVersion

	local resource_name, subscription_key_1 = GetLanguageStudioAccount(vcc)

	if not subscription_key_1 then
		return false, {code = 400, reason = "There is no Azure Language Studio subscription", errType = "inconcert"}
	end

	local uri = MakeCompleteURI("APPS", "global/languages?api-version=".. apiVersion .."&projectKind=Conversation")
	
	uri = uri:gsub("resourcename", resource_name)

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

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

	local success, response = request.Get(uri, additionalHeaders)
	if not success then
		LogError("[LUIS_APPS_40:LoadAppCultures] Loading Application Cultures. Error: '%s'", json.encode(response))
		return false, response
	end

	LogInfo("[LUIS_APPS40:LoadAppCultures] Success Loaded Application Cultures.")

	local encodedCultures = table.concat(response)

	LogDebug("[LUIS_APPS40:LoadAppCultures] Loaded Application Cultures, data: '%s'", encodedCultures)

	local data = json.decode(encodedCultures)

	local result = {}
	for _, item in ipairs(data.value) do
		table.insert(result, { name = item.languageName, code = item.languageCode })
	end

	-- Convertir la tabla a JSON
	local resultJson = json.encode(result)

	return true, resultJson
end

function PublishApp (appId, info, vcc) 
	assert(type(info) == "table")
	assert(info.appId)
	assert(type(vcc) == "string")

	local resource_name, subscription_key_1 = GetLanguageStudioAccount(vcc)

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

	local additionalHeaders = {
		["Ocp-Apim-Subscription-Key"] = subscription_key_1
	}
	
	local data = {
		trainedModelLabel = info.appId,
		assignedResourceIds = {}
	}

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

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

	LogInfo("[LUIS_APPS40:PublishApp] operationLocation: %s", operationLocation)

	local success = false
	local response = {}
	local retry = 0
	local TOTAL_RETRIES = 5
	local TIMEOUT_SEC = 5

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

		if not success then
			LogError("[LUIS_APPS40:PublishApp] Error Loading Publish Status on appId '%s'. Error: '%s'", appId, json.encode(response))
			return false, response
		end

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

		if parsedResponse.status == "succeeded" then
			parsedResponse.versionId = info.appId
			return true, json.encode(parsedResponse)
		else
			LogInfo("[LUIS_APPS40:PublishApp] Waiting %d seconds.", TIMEOUT_SEC)
			socket.sleep(TIMEOUT_SEC)
		end
	end

	LogInfo("[LUIS_APPS40:PublishApp] response: %s", json.encode(response))

	return false, json.encode(response)

end

function GetTrainApp (appId, versionId)
	assert(appId)
	assert(versionId)

	local uri = MakeCompleteURI("APPS", "%s/versions/%s/train", appId)

	local success, response = request.Get(uri)
	if not success then
		LogWarning("[LUIS_APPS40:GetTrainApp] Error retrieving TRAIN app '%s' versionId: '%s'. Error: '%s'", appId, versionId, json.encode(response))
		return false, response
	end

	local encodedSubscription = table.concat(response)

	LogInfo("[LUIS_APPS40:GetTrainApp] Success retrieving TRAIN app '%s' versionId: '%s'. Subscription Info: %s", appId, versionId, encodedSubscription)

	return true, encodedSubscription
end

function TrainApp (info, vcc) 
	assert(type(info) == "table")
	assert(info.appId)
	assert(type(vcc) == "string")

	local resource_name, subscription_key_1 = GetLanguageStudioAccount(vcc)

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

	local additionalHeaders = {
		["Ocp-Apim-Subscription-Key"] = subscription_key_1
	}
	
	local data = {
		modelLabel = info.appId,
		trainingMode = "standard",
		trainingConfigVersion = m_defaultTrainingConfigVersion,
		evaluationOptions = {
			kind = "manual",
			testingSplitPercentage = 20,
			trainingSplitPercentage = 80
		}
	}

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

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

	LogInfo("[LUIS_APPS40:TrainApp] operationLocation: %s", operationLocation)

	local response_train = {}
	local convertedJson = {
		statusId = "3",
		operationLocation = operationLocation,
		api_version = m_defaultApiVersion
	}

	table.insert(response_train, convertedJson)
	
	LogInfo("[LUIS_APPS40:TrainApp] response: %s", json.encode(response_train))

	return true, response_train

end

function ExecuteApiLCU (endpointUrl, message, appId, subscriptionKey, language, verbose, timezone) 

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

	local participantId = "no_required"
	
	local data = {
		kind = "Conversation",
		analysisInput = {
		   conversationItem = {
			  id = participantId,
			  text = message,
			  modality = "text",
			  language = language,
			  participantId = participantId
		   }
		},
		parameters = {
		   projectName = appId,
		   verbose = verbose,
		   deploymentName = appId,
		   stringIndexType = "TextElement_V8"
		}
	 }

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

	local decodedResponse = json.decode(response[1])

	if decodedResponse and decodedResponse.result then
		local score = 0
		for _, intent in ipairs(decodedResponse.result.prediction.intents) do
			intent.intent = intent.category
			intent.score = intent.confidenceScore
			if intent.category == decodedResponse.result.prediction.topIntent then
				score = intent.confidenceScore
			end
		end

		local response = {
			query = decodedResponse.result.query,
			topScoringIntent = {
			intent = decodedResponse.result.prediction.topIntent,
			score = score
			},
			intents = decodedResponse.result.prediction.intents,
			entities = decodedResponse.result.prediction.entities
		}

		return true, response

	else
		LogError("[LUIS_APPS40:ExecuteApiLCU] Error: '%s'", json.encode(response))
		return false, response
	end
end

function GetTrainStatus (info, vcc, operationLocation) 

	assert(type(info) == "table")
	assert(info.appId)
	assert(type(vcc) == "string")
	assert(type(operationLocation) == "string")

	local resource_name, subscription_key_1 = GetLanguageStudioAccount(vcc)

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

	local success, response = request.Get(operationLocation, additionalHeaders)

	if not success then
		LogError("[LUIS_APPS40:GetTrainStatus] Error loading app '%s'. Error: '%s'", appId, json.encode(response))
		return false, response
	end

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

	LogInfo("GetTrainStatus parsedResponse: %s", json.encode(parsedResponse))
	LogInfo("GetTrainStatus parsedResponse.status: %s", parsedResponse.status)

	local statusId = ""

	if parsedResponse.status == "succeeded" then
		statusId = "0"
	elseif parsedResponse.status == "error" or parsedResponse.status == "failed" then
		statusId = "1"
	else
		statusId = "3"
	end

	local response_train = {}
	local convertedJson = {
		statusId = statusId,
		operationLocation = operationLocation,
		api_version = m_defaultApiVersion,
		jobId = parsedResponse.jobId,
		result = parsedResponse.result
	}

	table.insert(response_train, convertedJson)
	
	LogInfo("[LUIS_APPS40:GetTrainStatus] response: %s", json.encode(response_train))

	return true, response_train

end

function GetAppVersion (appId, versionId, vcc) 	
	assert(appId)
	assert(vcc)

	local resource_name, subscription_key_1 = GetLanguageStudioAccount(vcc)

	local uri = MakeCompleteURI("APPS", appId .."/models?api-version=2022-10-01-preview")
	uri = uri:gsub("resourcename", resource_name)

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

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

	local success, response, headers = request.Get(uri, additionalHeaders)

	if not success then
		LogError("[LUIS_APPS40:GetAppVersion] GetAppVersion Error: '%s'", json.encode(response))
		return false, response
	end

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

	LogInfo("[LUIS_APPS40:GetAppVersion] response: %s", json.encode(parsedResponse))

	return true, parsedResponse

end

function LoadAppVersions (appId, paginated, vcc)
	assert(appId)
	assert(vcc)

	local resource_name, subscription_key_1 = GetLanguageStudioAccount(vcc)

	local uri = MakeCompleteURI("APPS", appId .."/models?api-version=2022-10-01-preview")
	uri = uri:gsub("resourcename", resource_name)

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

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

	local success, response, headers = request.Get(uri, additionalHeaders)

	if not success then
		LogError("[LUIS_APPS40:LoadAppVersions] LoadAppVersions Error: '%s'", json.encode(response))
		return false, response
	end

	local encodedVersions = table.concat(response)
	local parsedVersions = json.decode(encodedVersions)
	parsedVersions.nextLink = ""
	parsedVersions.api_version = m_defaultApiVersion

	if type(parsedVersions.value) == "table" and #parsedVersions.value == 0 then
		local value = {
			version = appId,
			label = appId
		}
		table.insert(parsedVersions.value, value)
	end
	
	-- get deployments
	local uri_deployments = MakeCompleteURI("APPS", appId .."/deployments?api-version=2022-10-01-preview")
	uri_deployments = uri_deployments:gsub("resourcename", resource_name)

	local success_deployments, response_deployments = request.Get(uri_deployments, additionalHeaders)
	local encodedDeployments = table.concat(response_deployments)
	local parsedDeployments = json.decode(encodedDeployments)

	if success_deployments and type(parsedDeployments) == "table" then

		for _, item in ipairs(parsedDeployments.value) do
			if item.deploymentName == appId then
				parsedVersions.lastDeployedDateTime = item.lastDeployedDateTime				
				parsedVersions.modelId = item.modelId
			end
		end

		parsedVersions.endpointHitsCount = #parsedDeployments.value

	end

	local success_get_app, encodedAppInfo_get_app = Get(appId, vcc)

	if success_get_app then

		LogInfo("encodedAppInfo_get_app: %s", json.encode(encodedAppInfo_get_app))

		local parsedGetApp = json.decode(encodedAppInfo_get_app)

		for i, intent in ipairs(parsedGetApp.intents) do
			intent.id = intent.category
			intent.name = intent.category
			intent.typeId = intent.compositionSetting
			intent.API_VERSION = m_defaultApiVersion
		end

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

		parsedVersions.intentsCount = parsedGetApp.intentsCount
		parsedVersions.intents = parsedGetApp.intents
		parsedVersions.entitiesCount = parsedGetApp.entitiesCount
		parsedVersions.entities = parsedGetApp.entities
		parsedVersions.utterancesCount = parsedGetApp.utterancesCount
		parsedVersions.utterances = parsedGetApp.utterances

	end

	LogInfo("parsedVersions: %s", json.encode(parsedVersions))

	local encodedVersionsResponse = parsedVersions
	return true, json.encode(encodedVersionsResponse)

end

function GetAppTrainJobs (appId, vcc) 
	
	assert(appId)
	assert(vcc)

	local resource_name, subscription_key_1 = GetLanguageStudioAccount(vcc)

	local uri = MakeCompleteURI("APPS", appId .."/train/jobs?api-version=2022-10-01-preview")
	uri = uri:gsub("resourcename", resource_name)

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

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

	local success, response, headers = request.Get(uri, additionalHeaders)

	if not success then
		LogError("[LUIS_APPS40:GetAppTrainJobs] Error: '%s'", json.encode(response))
		return false, response
	end

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

	return true, parsedResponse
end

function ExportApp (appId, versionId, exportFormat)
	assert(appId)

    local uri = MakeCompleteURI("APPS", "%s/versions/%s/export", appId)
    if exportFormat then
        uri = uri .. "?" .. exportFormat
    end
    exportFormat = exportFormat or "json"

	local success, response = request.Get(uri)
	if not success then
		LogError("[LUIS_APPS40:ExportApp] Error exporting APPLICATION VERSIONS on appId '%s' versionId: '%s', format: '%s'. Error: '%s'", appId, versionId, exportFormat, json.encode(response))
		return false, response
	end

	LogInfo("[LUIS_APPS40:ExportApp] Success Exporting APPLICATION VERSIONS on appId '%s' versionId: '%s' format: '%s'.", appId, versionId, exportFormat)

	local encodedExportedApp = table.concat(response)
	local exportedApp = json.decode(encodedExportedApp)
	return true, encodedExportedApp, exportedApp
end

function ImportApp (exportedApp, optionalName, apiVersion)
	local uri = MakeCompleteURI("APPS", "import", apiVersion)--https://westus.api.cognitive.microsoft.com/luis/api/v2.0/apps/import
	--Forzamos el cambio de endpoint a la version 3.0
	--local uri = "https://westus.api.cognitive.microsoft.com/luis/authoring/v3.0/apps/import"
	uri = uri:gsub("api/v2.0", "authoring/v3.0")
	if optionalName then
		uri = uri .. "?" .. optionalName
	end
	optionalName = optionalName or exportedApp.name
	LogInfo("[LUIS_APPS40:ImportApp] Importing APPLICATION to URL '%s'.", uri)
	local success, response = request.Post(uri,exportedApp)
	if not success then
		LogError("[LUIS_APPS40:ImportApp] Error importing APPLICATION %s. Error: '%s'", json.encode(exportedApp), json.encode(response))
		return false, response
	end
	local appId =  table.concat(response)
	LogInfo("[LUIS_APPS40:ImportApp] Success Importing APPLICATION on appId '%s'.", appId)
	return true, appId
end

function DeleteAppSuggestedUtterance (appId, utteranceName, versionId)
	assert(appId)
	assert(utteranceName)
	assert(versionId)

	local uri = MakeCompleteURI("APPS", "%s/versions/%s/suggest", appId)

	-- La palabra tiene que ir encerrada entre comillas porque asi lo espera el API
	utteranceName = "'" .. utteranceName .. "'"
	--[[
		Aunque en la documentacion indica que el request tiene que ser "text/plain", si se agrega ese contentType, falla y nos dice que tenemos
		que pasar el contentType "application/json" .......... PFFFFFFFFFFF
	]]--
	local success, response = request.Delete(uri, utteranceName, "application/json")
	if not success then
		LogError("[LUIS_APPS40:DeleteAppSuggestedUtterance] Error deleting UTTERANCE NAME '%s' on appId '%s' versionId: '%s'. Response: '%s'", utteranceName, appId, versionId, json.encode(response))
		return false, response
	end

	LogInfo("[LUIS_APPS40:DeleteAppSuggestedUtterance] Success deleted UTTERANCE NAME '%s' on appId '%s' versionId: '%s'.", utteranceName, appId, versionId)

	return true, response
end

function AssignAzureAccountsResourceToApp (appId, vcc, apiVersion)
	assert(type(appId) == "string")
	assert(type(vcc) == "string")

	LogInfo("[LUIS_APPS40:AssignAzureAccountsResourceToApp] Assigning Azure Account to app: '%s', vcc: '%s'.", appId, vcc)

	local success, vccAzureAccount, tokenInfo = azure.LoadAzureAccountAndToken(vcc)
	if not success then
		local err = vccAzureAccount
		LogError("[LUIS_APPS40:AssignAzureAccountsResourceToApp] Error Retrieving Azure Account & Token. Error: '%s'", err)
		return false, err
	end

	-- "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJ .... .... ... qaApUOSYbuMKsiD9Z7088dXwA"
	local additionalHeaders = {
		["Authorization"] = tokenInfo.token_type .. " " .. tokenInfo.access_token
	}

	local uri = MakeCompleteURI("APPS", "%s/azureaccounts", appId)

	local data = {
		azureSubscriptionId = vccAzureAccount.AzureSubscriptionId, -- "dc506e4a-ff4a-4178-9e18-1750b4247451"
		resourceGroup = vccAzureAccount.ResourceGroup, -- "inconcert_luis_grp",
		accountName = vccAzureAccount.AccountName -- "inconcert_luis"
	}

	local success, response = request.Post(uri, data, nil, additionalHeaders)
	if not success then
		LogError("[LUIS_APPS40:AssignAzureAccountsResourceToApp] Error Assigning Azure Accounts to app '%s'. Error: '%s'", appId, json.encode(response))
		return false, response
	end

	LogInfo("[LUIS_APPS40:AssignAzureAccountsResourceToApp] Success Assigned Azure Accounts to app '%s'.", appId)

	return true, response
end

function UnassignAzureAccountsResourceOfApp (appId, vcc, apiVersion)
	assert(type(appId) == "string")
	assert(type(vcc) == "string")

	LogInfo("[LUIS_APPS40:UnassignAzureAccountsResourceOfApp] Unassigning Azure Account of app: '%s', vcc: '%s'.", appId, vcc)

	local success, vccAzureAccount, tokenInfo = azure.LoadAzureAccountAndToken(vcc)
	if not success then
		local err = vccAzureAccount
		LogError("[LUIS_APPS40:UnassignAzureAccountsResourceOfApp] Error Retrieving Azure Account & Token. Error: '%s'", err)
		return false, err
	end

	local additionalHeaders = {
		["Authorization"] = tokenInfo.token_type .. " " .. tokenInfo.access_token
	}

	local data = {
		azureSubscriptionId = vccAzureAccount.AzureSubscriptionId, -- "dc506e4a-ff4a-4178-9e18-1750b4247451"
		resourceGroup = vccAzureAccount.ResourceGroup, -- "inconcert_luis_grp",
		accountName = vccAzureAccount.AccountName -- "inconcert_luis"
	}

	local uri = MakeCompleteURI("APPS", "%s/azureaccounts", appId)

	local success, response = request.Delete(uri, data, nil, additionalHeaders)
	if not success then
		LogError("[LUIS_APPS40:UnassignAzureAccountsResourceOfApp] Error Unassigning Azure Account Resource of App '%s'. Error: '%s'", appId, json.encode(response))
		return false, response
	end

	response = table.concat(response)
	LogInfo("[LUIS_APPS40:UnassignAzureAccountsResourceOfApp] Success Unassigned Azure Account Resource of appId '%s'. Response", appId)

	return true, response
end

function GetAssignedAzureAccountsResource (appId, tenantInfo, apiVersion)
	assert(type(appId) == "string")
	assert(type(tenantInfo) == "table")

	LogInfo("[LUIS_APPS40:GetAssignedAzureAccountsResource] Retrieving Azure Account in app: '%s', tenantInfo: '%s'.", appId, json.encode(tenantInfo))

	local success, tokenInfo = azure.GetAzureResourceManagerToken(tenantInfo)
	--LogDebug("[LUIS_AZURE:GetAssignedAzureAccountsResource] Azure Resource Manager Token. success: '%s', token: '%s'", success, tokenInfo)

	if not success then
		local err = tokenInfo
		LogError("[LUIS_AZURE:GetAssignedAzureAccountsResource] Error Retrieving Azure Token. App: '%s'. Error: '%s'", appId, err)
		return false, err
	end

	local additionalHeaders = {
		["Authorization"] = tokenInfo.token_type .. " " .. tokenInfo.access_token
	}

	local uri = MakeCompleteURI("APPS", "%s/azureaccounts", appId)

	local success, response = request.Get(uri, additionalHeaders)
	if not success then
		LogError("[LUIS_AZURE:GetAssignedAzureAccountsResource] Error Retrieving app: '%s' Azure Accounts Error: '%s'.", appId, json.encode(response))
		return false, response
	end

	LogInfo("[LUIS_AZURE:GetAssignedAzureAccountsResource] Success Retrieving Azure Accounts in app: '%s'.", appId)
	local encodedAzureAccountInApp = table.concat(response)
	LogDebug("[LUIS_AZURE:GetAssignedAzureAccountsResource] app: '%s', encodedAzureAccountInApp: '%s'", appId, encodedAzureAccountInApp)

	return true, encodedAzureAccountInApp
end
