/*
* Licensed Materials - Property of IBM* and/or HCL**
* UrbanCode Deploy
* (c) Copyright IBM Corporation 2013, 2017. All Rights Reserved.
* (c) Copyright HCL Technologies Ltd. 2018. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*
* * Trademark of International Business Machines
* ** Trademark of HCL Technologies Limited
*/

import org.apache.commons.lang3.StringUtils
import org.apache.commons.net.ftp.FTP
import org.apache.commons.net.ftp.FTPClient
import org.apache.commons.net.ftp.FTPFile
import org.apache.commons.net.ftp.FTPReply

import com.urbancode.air.AirPluginTool

// Get step properties
def apTool = new AirPluginTool(this.args[0], this.args[1])
inProps = apTool.getStepProperties()

String host           = inProps['host'].trim()
String port           = inProps['port'].trim()
String username       = inProps['username']
String password       = inProps['password']
String fileType       = inProps['fileType']
String filePath       = inProps['filePath'].trim()
String uploadDir      = inProps['uploadDir'].trim()
String fileRename     = inProps['fileRename'].trim()
String connectionMode = inProps['connectionMode']?.trim()

// Input sanitation
URI hostUri = null
try {
    hostUri = new URI('ftp://' + host)
}
catch (URISyntaxException use) {
    println ('[error]  Invalid URI syntax.')
    println ('[possible solution]  Please update the step configuration with a valid URI for Host.')
    System.exit(1)
}

if (!StringUtils.isNumeric(port)) {
    println ('[error]  The value of the Port property is not numeric.')
    println ('[possible solution]  Please update the step configuration with a numeric value for Port.')
    System.exit(1)
}

File uploadFile = new File(filePath)
if (!uploadFile.isFile()) {
    println ('[error]  File Path "' + filePath + '" is not a file')
    println ('[possible solution]  Please update the step configuration with a valid file path.')
    System.exit(1)
}

if (!uploadDir.startsWith('/')) {
    uploadDir = '/' + uploadDir
}

String uploadFileName = ''
if (fileRename) {
    uploadFileName = fileRename
}
else {
    uploadFileName = uploadFile.getName()
}

FTPClient ftp = new FTPClient()

// Connect
println ('[action]  Connecting to FTP Server with host: ' + hostUri.getHost() + ', port: ' + port + '...')
try {
    ftp.connect(hostUri.getHost(), port.toInteger())
}
catch (UnknownHostException uhe) {
    println ('[error]  Host "' + host + '" cannot be resolved.')
    println ('[possible solution]  Please update the step configuration with a valid host.')
    System.exit(1)
}
catch (ConnectException ce) {
    println ('[error]  Could not connect to FTP Server.')
    println ('[possible solution]  Ensure the step configuration Hostname property is entered correctly and the FTP server ' +
             'is actively listening for connections.')
    System.exit(1)
}
if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
    println ('[error]  FTP Server refused connection: ' + ftp.getReplyString())
    closeFTPConnection(ftp)
    System.exit(1)
}
println ('[ok]  Connected to FTP Server.')

try {
    // Login
    println ('[action]  Logging into FTP Server...')
    boolean loggedIn = ftp.login(username, password)
    if (!loggedIn) {
        println ('[error]  Could not log in to FTP Server: ' + ftp.getReplyString())
        closeFTPConnection(ftp)
        System.exit(1)
    }
    println ('[ok]  Logged into FTP Server.')

    if (connectionMode == "localPassive") {
        ftp.enterLocalPassiveMode()
        println ('[ok]  Set Local Passive data connection mode.')
    }
    else if (connectionMode == "localActive") {
        ftp.enterLocalActiveMode()
        println ('[ok]  Set Local Active data connection mode.')
    }

    // Set file type
    println ('[action]  Setting FTP File Type to FTP.' + fileType + '_FILE_TYPE...')
    boolean typeSet = false
    if (fileType.equals('ASCII')) {
        typeSet = ftp.setFileType(FTP.ASCII_FILE_TYPE)
    }
    else if (fileType.equals('BINARY')) {
        typeSet = ftp.setFileType(FTP.BINARY_FILE_TYPE)
    }
    else {
        println ('[error]  No FTP File Type selected. Please update the step configuration with a valid FTP File Type.')
        System.exit(1)
    }
    if (typeSet) {
        println ('[ok]  FTP File Type set to ' + fileType + '_FILE_TYPE.')
    }
    else {
        println ('[error]  FTP File Type could not be set to ' + fileType + '_FILE_TYPE: ' + ftp.getReplyString())
        System.exit(1)
    }

    // Prepare Directory
    if (!uploadDir.equals('/')) {
        ftp.changeWorkingDirectory(uploadDir)
        if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
            println ('[ok]  Directory ' + uploadDir + ' present on FTP Server')
        }
        else {
            println ('[error]  Directory ' + uploadDir + ' not present on FTP Server. Creating directory path...\n')

            // Create Directory
            String[] uploadPath = uploadDir.substring(1, uploadDir.length()).split('/')  // before split remove initial '/' because cannot check '/' directory
            String buildingDir = ''
            uploadPath.each { dir ->
                buildingDir += '/' + dir
                ftp.changeWorkingDirectory(buildingDir)
                if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
                    println ('[ok]  Directory ' + buildingDir + ' already created.')
                }
                else {
                    println ('[action]  Creating new directory "' + buildingDir + '".')
                    boolean dirCreated = ftp.makeDirectory(buildingDir)
                    if (!dirCreated) {
                        println ('[error]  Could not create directory "' + buildingDir + '": ' + ftp.getReplyString())
                        closeFTPConnection(ftp)
                        System.exit(1)
                    }
                    println ('[ok]  New directory "' + buildingDir + '" created.')
                }
            }
        }
    }
    if (!uploadDir.endsWith('/')) {
        uploadDir += '/'
    }

    // Upload
    InputStream iStream = new FileInputStream(uploadFile)

    println ('\n[action]  Starting to upload file ' + filePath + ' to ' + uploadDir + uploadFileName)
    boolean complete = ftp.storeFile(uploadDir + uploadFileName, iStream)
    iStream.close()
    if (complete) {
        println ('[ok]  File ' + uploadDir + uploadFileName + ' uploaded successfully')
        closeFTPConnection(ftp)
        System.exit(0)
    }
    else {
        println ('[error]  File ' + uploadDir + uploadFileName + ' failed to upload: ' + ftp.getReplyString())
        closeFTPConnection(ftp)
        System.exit(1)
    }
}
catch (IOException ioe) {
    println ('[error]  Exception caught. Attempting to gracefully close FTP Server connection before printing stacktrace and exiting...')
    ioe.printStackTrace()
}
finally {
    if (ftp.isConnected()) {
        closeFTPConnection(ftp)
        System.exit(1)
    }
    else {
        println ('[ok]  FTP Server connection already closed')
        System.exit(1)
    }
}

/**
*  @param ftp The FTPClient to close the connection to
*/
private void closeFTPConnection(FTPClient ftp) {
    try {
        ftp.logout()
        ftp.disconnect()
        println ('[ok]  FTP Server connection closed')
    }
    catch (IOException ioe) {
        println ('[error]  Could not properly logout and disconnect from FTP Server')
        ioe.printStackTrace()
        System.exit(1)
    }
}
