local ftp = require("socket.ftp")
local ltn12 = require("ltn12")

module(..., package.seeall)

-- in case it's available
local LogInfo = LogInfo or function(msg, ...)
	print("Info", msg:format(...))
end
local LogWarning = LogWarning or function(msg, ...)
	print("Warning", msg:format(...))
end

local function GetSchedulerModule()
	local scheduler = require("reporter.scheduler")
	return scheduler
end

---------------------------------------------------------------
---------------------------------------------------------------

--[[
Aca se realiza la creacion del directorio destino en FTP, en el cual se subiran los archivos.

@params
	directory  : Nombre del directorio destino.
	protocolConfig  : JSON con la configuracion y credenciales del FTP destino.
-
Upload FTP - Referencia: http://w3.impa.br/~diego/software/luasocket/ftp.html

https://en.wikipedia.org/wiki/List_of_FTP_commands

	Se probo contra un FTP configurado en "Windows Server 2008"
	Config:
		1 - Binding and SSL settings
			IP Addresses -> All Unassigned
			Puertos      -> 21 y/o 23
			SSL          -> No SSL
		2 - Authentication and Authorization Information
			Authentication
				Anonymous: true/false
				Basic: true/false
			Authorization
				Allow access to: All users
				Permissions: read/write
]]--
function CreateDirectory (parentDirectory, directory, protocolConfig)
	--LogInfo("[FTP:CreateDirectory] Create directory on FTP. Directory '%s', parent directory: '%s'.", directory, parentDirectory)

	if not directory or directory:len() == 0 then
		return true
	end
	if parentDirectory and parentDirectory:len() > 0 then
		directory = parentDirectory .. "/" .. directory
	end

	-- TODO: De momento se utiliza, ver si es necesario validar que exista el directorio.
	--FTPListContentDirectory(parentDirectory, protocolConfig)

	-- Si no se incluyen los parametros "user" & "password", la conexion la realiza con usuarios "anonymous",
	--  si es que el server ftp permite estas conexiones.
	--
	local params = {
		host = protocolConfig.Host,
		port = protocolConfig.Port,    -- no es requerido (por defecto es el puerto 21)
		user = protocolConfig.User,
		password = protocolConfig.Pwd,
		command = "MKD",          -- comando FTP "Make directory"
		argument = directory,
		source = ""
	}

	local res, success, err = pcall(ftp.put, params)

	--LogDebug("-CreateDirectory--> res: '%s', success: '%s', err: '%s'", res, success, err)

	if not res and not success then
		--LogWarning("[FTP:CreateDirectory] Error creating directory '%s' to FTP. Err: '%s', FTP host: '%s'.", directory, err, protocolConfig.Host)
	end

	return success, err
end

--[[
Aca se realiza el borrado de un directorio del FTP.

@params
	directory  : Nombre del directorio destino.
	protocolConfig  : JSON con la configuracion y credenciales del FTP destino.
-
Upload FTP - Referencia: http://w3.impa.br/~diego/software/luasocket/ftp.html

https://en.wikipedia.org/wiki/List_of_FTP_commands

	Se probo contra un FTP configurado en "Windows Server 2008"
	Config:
		1 - Binding and SSL settings
			IP Addresses -> All Unassigned
			Puertos      -> 21 y/o 23
			SSL          -> No SSL
		2 - Authentication and Authorization Information
			Authentication
				Anonymous: true/false
				Basic: true/false
			Authorization
				Allow access to: All users
				Permissions: read/write
]]--
function RemoveDirectory (parentDirectory, directory, protocolConfig)
	LogInfo("[FTP:RemoveDirectory] Removing directory on FTP. Directory '%s', parent directory: '%s'.", directory, parentDirectory)

	if not directory or directory:len() == 0 then
		return true
	end
	if parentDirectory and parentDirectory:len() > 0 then
		directory = parentDirectory .. "/" .. directory
	end

	-- Si no se incluyen los parametros "user" & "password", la conexion la realiza con usuarios "anonymous",
	--  si es que el server ftp permite estas conexiones.
	--
	local params = {
		host = protocolConfig.Host,
		port = protocolConfig.Port,    -- no es requerido (por defecto es el puerto 21)
		user = protocolConfig.User,
		password = protocolConfig.Pwd,
		command = "RMD",          -- comando FTP "Remove directory"
		argument = directory,
		source = ""
	}

	local res, success, err = pcall(ftp.put, params)

	--LogDebug("-RemoveDirectory--> res: '%s', success: '%s', err: '%s'", res, success, err)

	if not res and not success then
		LogWarning("[FTP:RemoveDirectory] Error Removing directory '%s' to FTP. Err: '%s', FTP Host: '%s'.", directory, err, protocolConfig.Host)
	end

	return success, err
end


--[[
Aca se realiza el UPLOAD del arhivo al FTP.
El archivo se procesa en forma de BLOB o de string.

@params
	fileName   : Nombre del archivo destino.
	content    : Contenido del archivo.
	fileType   : Tipo de formato del archivo recibido ("string" o "blob")
	protocolConfig  : JSON con la configuracion y credenciales del FTP destino.
-
Upload FTP - Referencia: http://w3.impa.br/~diego/software/luasocket/ftp.html

	Se probo contra un FTP configurado en "Windows Server 2008"
	Config:
		1 - Binding and SSL settings
			IP Addresses -> All Unassigned
			Puertos      -> 21 y/o 23
			SSL          -> No SSL
		2 - Authentication and Authorization Information
			Authentication
				Anonymous: true/false
				Basic: true/false
			Authorization
				Allow access to: All users
				Permissions: read/write
]]--
function UploadFile (fileName, content, fileType, protocolConfig, rootDirectory, scheduleToRun, utils, entry)
	
	local localFile = entry
	--------------------
	
	if rootDirectory and rootDirectory:len() > 0 then
		fileName = rootDirectory .. "/" .. fileName
	end
	--LogDebug("[FTP:UploadFile] Upload file: '%s' to FTP.", fileName)
	
	-- por defecto es blob
	local fn = fileType == "string" and ltn12.source.string or ltn12.source.file

	-- Si no se incluyen los parametros "user" & "password", la conexion la realiza con usuarios "anonymous",
	--  si es que el server ftp permite estas conexiones.
	--
	local params = {
		host = protocolConfig.Host,
		port = protocolConfig.Port,    -- no es requerido (por defecto es el puerto 21)
		user = protocolConfig.User,
		password = protocolConfig.Pwd,
		command = "STOR",         -- comando FTP "STORE"
		argument = fileName,
		source = fn(content)
	}
	local port = protocolConfig.Port or 21
	local curlCommand = "curl -T " .. localFile .. " ftp://" .. protocolConfig.Host .. ":" .. port .. "/" .. fileName .. " --user " .. protocolConfig.User .. ":" .. protocolConfig.Pwd
	local curlCommandToLog = "curl -T " .. localFile .. " ftp://" .. protocolConfig.Host .. ":" .. port .. "/" .. fileName .. " --user " .. protocolConfig.User .. ":*******"
	--LogDebug("[FTP:UploadFile] Curl command to upload file: %s",curlCommandToLog)
	os.execute(curlCommand)
	local fileSize = 10000

	--local res, fileSize, err = pcall(ftp.put, params)

	--LogDebug("-UploadFile--> res: '%s', fileSize: '%s', err: '%s'", res, fileSize, err)

	if not fileSize then
		LogError("[FTP:UploadFile] Error uploading file '%s' to FTP. Err: '%s', FTP host: '%s'.", fileName, err, protocolConfig.Host)
	end

	return fileSize, err
end
--[[
Funcion que valida si el FTP esta lista para ser utilizado.
Conexion basica contra el host, user y password.

@params
	protocolConfig  : JSON con la configuracion y credenciales del FTP destino.
-
Upload FTP - Referencia: http://w3.impa.br/~diego/software/luasocket/ftp.html

https://en.wikipedia.org/wiki/List_of_FTP_commands

	Se probo contra un FTP configurado en "Windows Server 2008"
	Config:
		1 - Binding and SSL settings
			IP Addresses -> All Unassigned
			Puertos      -> 21 y/o 23
			SSL          -> No SSL
		2 - Authentication and Authorization Information
			Authentication
				Anonymous: true/false
				Basic: true/false
			Authorization
				Allow access to: All users
				Permissions: read/write
]]--
function IsReady (schedule)
	local protocolConfig = schedule.ftp or {}

	LogInfo("[FTP:IsReady] Validate connection to FTP. FTP host: '%s'.", protocolConfig.Host)

	if not protocolConfig.Host then
		LogError("[FTP:IsReady] FTP host is required.")
		return false, "Incomplete definition. FTP host is required."
	end
	if not protocolConfig.User then
		LogError("[FTP:IsReady] FTP user is required.")
		return false, "Incomplete definition. FTP user is required."
	end
	if not protocolConfig.Pwd then
		LogError("[FTP:IsReady] FTP pwd is required.")
		return false, "Incomplete definition. FTP pwd file is required."
	end

	-- Si no se incluyen los parametros "user" & "password", la conexion la realiza con usuarios "anonymous",
	--  si es que el server ftp permite estas conexiones.
	--
	local info = {}
	local params = {
		host = protocolConfig.Host,
		port = protocolConfig.Port,     -- no es requerido (por defecto es el puerto 21)
		user = protocolConfig.User,
		password = protocolConfig.Pwd,
		command = "NOOP",          -- comando FTP "NOOP" No operation (dummy packet; used mostly on keepalives)
		sink = ltn12.sink.table(info)
	}

	local res, response, err = pcall(ftp.get, params)
	info = table.concat(info)

	if not res or not response then
		LogError("[FTP:IsReady] Connection error to FTP. Err: '%s', info: '%s', FTP host: '%s'.", err, info, protocolConfig.Host)
		return false, err
	end

	LogInfo("[FTP:IsReady]--> Successful connection to FTP host: '%s'. FTP Response: '%s'", protocolConfig.Host, info)

	return true
end

--[[
	Funcion que valida si se puede escribir en el directorio destino del FTP.
]]--
function WriteValidation (schedule, rootDestinationDirectory)
	local protocolConfig = schedule.ftp or {}

	LogInfo("[FTP:WriteValidation] Write validation connection to FTP. FTP host: '%s', rootDestinationDirectory: '%s'.", protocolConfig.Host, rootDestinationDirectory)
	--LogDebug("[FTP:WriteValidation] ---> Schedule config: '%s'.", json.encode(schedule))

	local tempDir = "__TMP_" .. uuid.new()
	local success, err = CreateDirectory(rootDestinationDirectory, tempDir, protocolConfig)

	if not success then
		LogInfo("[FTP:WriteValidation] Not have write permissions on FTP. Msg: '%s', FTP Host: '%s'.", err, protocolConfig.Host)
		return false, "Not have write permissions on FTP"
	end

	RemoveDirectory(rootDestinationDirectory, tempDir, protocolConfig)
	return true
end
