/*******************************************************************************
 *  Imixs Workflow Technology
 *  Copyright (C) 2001, 2008 Imixs Software Solutions GmbH,  
 *  http://www.imixs.com
 *  
 *  This program is free software; you can redistribute it and/or 
 *  modify it under the terms of the GNU General Public License 
 *  as published by the Free Software Foundation; either version 2 
 *  of the License, or (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful, 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 *  General Public License for more details.
 *  
 *  You can receive a copy of the GNU General Public
 *  License at http://www.gnu.org/licenses/gpl.html
 *  
 *  Contributors:  
 *  	Imixs Software Solutions GmbH - initial API and implementation
 *  	Ralph Soika
 *******************************************************************************/
package com.alexanderlogistics;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import java.util.logging.Logger;

import javax.ejb.Stateless;
import javax.inject.Inject;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPSClient;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.imixs.workflow.FileData;
import org.imixs.workflow.exceptions.PluginException;

/**
 * The FTPConnector service provides methods to push invoices to cargosoft
 * 
 * @version 1.0
 * @author rsoika
 */

@Stateless
public class FTPConnector {

    public static final String FTP_ERROR = "FTP_ERROR";
    public static final String ENV_EXPORT_FTP_HOST = "cargosoft.export.ftp.host";
    public static final String ENV_EXPORT_FTP_PATH = "cargosoft.export.ftp.path";
    public static final String ENV_EXPORT_FTP_PORT = "cargosoft.export.ftp.port";
    public static final String ENV_EXPORT_FTP_USER = "cargosoft.export.ftp.user";
    public static final String ENV_EXPORT_FTP_PASSWORD = "cargosoft.export.ftp.password";
    
    public static final String ENV_EXPORT_FTP_MAX_ATTACHMENT_SIZED = "cargosoft.export.ftp.maxattachmentsize";

    private static Logger logger = Logger.getLogger(FTPConnector.class.getName());

    @Inject
    @ConfigProperty(name = ENV_EXPORT_FTP_HOST)
    Optional<String> ftpServer;

    @Inject
    @ConfigProperty(name = ENV_EXPORT_FTP_PATH)
    Optional<String> ftpPath;

    @Inject
    @ConfigProperty(name = ENV_EXPORT_FTP_PORT, defaultValue = "21")
    Optional<Integer> ftpPort;

    @Inject
    @ConfigProperty(name = ENV_EXPORT_FTP_USER)
    Optional<String> ftpUser;

    @Inject
    @ConfigProperty(name = ENV_EXPORT_FTP_PASSWORD)
    Optional<String> ftpPassword;

    /**
     * This method transfers a snapshot to a ftp server.
     * 
     * @param fileData object
     * @throws PluginException
     */
    public void put(FileData fileData) throws PluginException {

        if (!ftpServer.isPresent()) {
            throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
                    "FTP file transfer failed: no ftp host name provided (" + ENV_EXPORT_FTP_HOST + ")!");
        }
        
        String fileName = fileData.getName();

        // Compute file path
        String ftpWorkingPath = ftpPath.get();
        if (!ftpWorkingPath.startsWith("/")) {
            ftpWorkingPath = "/" + ftpWorkingPath;
        }
        if (!ftpWorkingPath.endsWith("/")) {
            ftpWorkingPath = ftpWorkingPath + "/";
        }
        InputStream writer = null;

        FTPClient ftpClient = null;
        try {
            logger.finest("......put " + fileName + " to FTP server: " + ftpServer + "...");
            ftpClient = new FTPSClient("TLS", false);
            ftpClient.setBufferSize(8192);
            ftpClient.connect(ftpServer.get(), ftpPort.get().intValue());
            if (ftpClient.login(ftpUser.get(), ftpPassword.get()) == false) {
                throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
                        "FTP file transfer failed: login failed!");
            }
            ftpClient.enterLocalPassiveMode();
            ftpClient.setFileType(FTP.ASCII_FILE_TYPE);
            ftpClient.setControlEncoding("UTF-8");

            // verify directories
            if (!ftpClient.changeWorkingDirectory(ftpWorkingPath)) {
                throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
                        "FTP file transfer failed: missing working directory '" + ftpWorkingPath + "' : "
                                + ftpClient.getReplyString());
            }

            // upload file to FTP server.
            writer = new ByteArrayInputStream(fileData.getContent());

            if (!ftpClient.storeFile(fileName, writer)) {
                throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
                        "FTP file transfer failed: unable to write '" + ftpWorkingPath + fileName + "' : "
                                + ftpClient.getReplyString());
            }

            logger.finest("...." + ftpWorkingPath + fileName + " transfered successfull to " + ftpServer);

        } catch (IOException e) {
            throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
                    "FTP file transfer failed: " + e.getMessage(), e);
        } finally {
            // do logout....
            try {
                if (writer != null) {
                    writer.close();
                }
                ftpClient.logout();
                ftpClient.disconnect();
            } catch (IOException e) {
                throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
                        "FTP file transfer failed: " + e.getMessage(), e);
            }
        }
    }

    /**
     * This method reads data form the current working directory
     * 
     * @param snapshot
     * @throws ArchiveException
     * @return data
     */
    public byte[] get(FTPClient ftpClient, String fileName) throws PluginException {
        if (ftpClient == null) {
            throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
                    "FTP file transfer failed: no ftpClient provided!");
        }

        long l = System.currentTimeMillis();
        ByteArrayOutputStream bos = null;
        try {
            bos = new ByteArrayOutputStream();
            ftpClient.retrieveFile(fileName, bos);
            byte[] result = bos.toByteArray();

            logger.finest("......" + fileName + " transfered successfull from " + ftpServer + " in "
                    + (System.currentTimeMillis() - l) + "ms");
            return result;
        } catch (IOException e) {
            throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
                    "FTP file transfer failed: " + e.getMessage(), e);
        } finally {
            // do logout....
            try {
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
                        "FTP file transfer failed: " + e.getMessage(), e);
            }
        }
    }

    /**
     * This method changes the current working sub-directy. If no corresponding
     * directory exits the method creats one.
     * 
     * @throws ArchiveException
     */
    @SuppressWarnings("unused")
    private void changeWorkingDirectory(FTPClient ftpClient, String subDirectory) throws PluginException {
        // test if we have the subdreictory
        try {
            if (!ftpClient.changeWorkingDirectory(subDirectory)) {
                // try to creat it....
                if (!ftpClient.makeDirectory(subDirectory)) {
                    throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
                            "FTP Error: unable to create sub-directory '" + subDirectory + "' : "
                                    + ftpClient.getReplyString());
                }
                ftpClient.changeWorkingDirectory(subDirectory);
            }
        } catch (IOException e) {
            throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
                    "FTP file transfer failed: " + e.getMessage(), e);
        }
    }

}
