package com.huatek.busi.common.util.sftp;

import cn.hutool.core.util.StrUtil;
import com.huatek.busi.common.util.DateUtil;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import java.io.*;
import java.util.Properties;
import java.util.Vector;

@Slf4j
public class SFTPTool {

    /**
     * PNG图片转成的字符串
     */
    public static String streamStr = " ";

    /**
     * 1. 第一步:连接sftp服务器,先获取Session
     *
     * @param host     主机
     * @param port     端口
     * @param username 用户名
     * @param password 密码
     * @return
     */
    public static Session getSession(String host, int port, String username, String password) {
        Session session = null;
        try {
            JSch jsch = new JSch();
            session = jsch.getSession(username, host, port);
            log.info("Session created.");
            session.setPassword(password);
            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
            session.setConfig(sshConfig);
            session.connect();
            log.info("Session connected.");
        } catch (Exception e) {
            e.printStackTrace();
            if (session != null && session.isConnected()) {
                session.disconnect();
            }
        }
        return session;
    }

    public static ChannelSftp sftpConnection(String host, int port, String userName, String password) {
        JSch jsch = new JSch();
        ChannelSftp channelSftp = null;
        try {
            jsch.getSession(userName, host, port);
            Session sshSession = jsch.getSession(userName, host, port);
            sshSession.setPassword(password);
            Properties properties = new Properties();
            properties.put("StrictHostKeyChecking", "no");
            sshSession.setConfig(properties);
            sshSession.connect();
            Channel channel = sshSession.openChannel("sftp");
            channel.connect();
            channelSftp = (ChannelSftp) channel;
        } catch (JSchException e) {
            e.printStackTrace();
        }
        return channelSftp;
    }


    /**
     * 2.第二步: 连接sftp服务器,再获取链接
     *
     * @return
     */
    public ChannelSftp getConnect(Session session) {
        ChannelSftp sftp = null;
        try {
            if (session == null) {
                log.info("Can't Create Connect,Because session is null");
                return null;
            }
            Channel channel = session.openChannel("sftp");
            log.info("Opening Channel.");
            channel.connect();
            sftp = (ChannelSftp) channel;
            log.info("Connected to " + session.getHost() + ":" + session.getPort());
        } catch (Exception e) {
            e.printStackTrace();
            if (sftp != null && sftp.isConnected()) {
                sftp.disconnect();
            }
        }
        return sftp;
    }

    /**
     * 3.第三步:关闭 channel和session
     *
     * @param channel
     */
    public void disconnect(Channel channel, Session session) {
        try {
            if (channel != null && channel.isConnected()) {
                channel.disconnect();
                log.info("Disconnected channel");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            if (session != null && session.isConnected()) {
                session.disconnect();
                log.info("Disconnected session");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 上传文件到远端服务器,如果在同一目录下,文件名相同会自动替换
     * 如果上传一半,网络原因中断,那服务器上会有一半大小的文件,请重新上传.
     *
     * @param destDirectory 远端服务器要上传的目录  : /data/temp/test/
     * @param srcDirectory  本地要上传的目录 : D:/test/
     * @param srcFileName   本地要上传的文件 : upload.txt
     * @param sftp
     */
    public static String upload(String destDirectory, String srcDirectory, String srcFileName, String status, ChannelSftp sftp) {
        try {
            sftp.cd(destDirectory);
        } catch (SftpException sftpException) {
            sftpException.printStackTrace();
        }

        try {
            sftp.ls(status);
            sftp.cd(status);
        } catch (Exception e) {
            try {
                sftp.mkdir(status);
                sftp.cd(status);
            } catch (SftpException sftpException) {
                sftpException.printStackTrace();
            }
        }

        try {
            File file = new File(srcDirectory + srcFileName);
            if (!file.exists()) {
                throw new Exception(srcDirectory + srcFileName + " is not exists");
            }
            log.info("上传本地文件" + srcDirectory + srcFileName + "到远端服务器" + destDirectory + " 开始");
//            sftp.put(new FileInputStream(file), file.getName());
            InputStream inputStream = new FileInputStream(file);
            OutputStream outputStream = sftp.put(file.getName());
            int b;
            byte[] bytes = new byte[1024];
            while ((b = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, b);
            }
            outputStream.flush();
            inputStream.close();
            outputStream.close();
            log.info("上传本地文件" + srcDirectory + srcFileName + "到远端服务器" + destDirectory + " 结束");
            return destDirectory + "/" + status;
        } catch (Exception e) {
            log.error("<<<<<<<<<<<<<上传文件异常>>>>>>>>>>>>>>>>>>");
        }
        return null;
    }

    /**
     * 上传文件到远端服务器,如果在同一目录下,文件名相同会自动替换
     * 如果上传一半,网络原因中断,那服务器上会有一半大小的文件,请重新上传.
     *
     * @param destDirectory 远端服务器要上传的目录  : /data/temp/test/
     * @param srcDirectory  本地要上传的目录 : D:/test/
     * @param srcFileName   本地要上传的文件 : upload.txt
     * @param sftp
     */
    public static void upload(String destDirectory, String srcDirectory, String srcFileName, ChannelSftp sftp) throws Exception {
        try {
            sftp.cd(destDirectory);
            File file = new File(srcDirectory + srcFileName);
            if (!file.exists()) {
                throw new Exception(srcDirectory + srcFileName + " is not exists");
            }
            System.out.println("上传本地文件" + srcDirectory + srcFileName + "到远端服务器" + destDirectory + " 开始");
            sftp.put(new FileInputStream(file), file.getName());
            System.out.println("上传本地文件" + srcDirectory + srcFileName + "到远端服务器" + destDirectory + " 结束");
//            sftp.put("D:/application/eclipse64ee/workspace/SFTP/src/com/testdemo/www/ftp/SFTPTooL.java","/data/temp/test");//将本地目录的文件直接上传到服务器上
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    /**
     * 上传流到远端服务器,如果在同一目录下,文件名相同会自动替换
     * 如果上传一半,网络原因中断,那服务器上会有一半大小的文件,请重新上传.
     *
     * @param destDirectory 远端服务器要上传的目录  : /data/temp/test/
     * @param srcStream     本地要上传的流 : D:/test/
     * @param srcFileName   本地指定到远端服务器要生成的文件名 : upload.txt
     * @param count         上传次数
     * @param isUse         是否是删除文件夹：use文件夹、delete文件夹
     * @param sftp
     * @throws Exception
     */
    public static String upload(String destDirectory, String folderPath, InputStream srcStream, String srcFileName, Integer count, String isUse, ChannelSftp sftp) {
        try {
            folderPath = folderPath + File.separator + isUse + File.separator + DateUtil.getYYMMDDTime() + File.separator + count;
            mkdir(destDirectory, folderPath, sftp);

            if (srcStream == null) {
                throw new Exception("流为空," + srcFileName + " is not exists");
            }
            log.info("上传流" + srcFileName + "到远端服务器" + count + " 开始");
//            sftp.put(srcStream, srcFileName);
            OutputStream outputStream = sftp.put(srcFileName);
            int b;
            byte[] bytes = new byte[1024];
            while ((b = srcStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, b);
            }
            outputStream.flush();
            srcStream.close();
            outputStream.close();
            log.info("上传流" + srcFileName + "到远端服务器" + count + " 结束");
        } catch (Exception e) {
            log.error("<<<<<<<<<<<<<<上传异常>>>>>>>>>>>>>>>>>>>");
        }
        return destDirectory + File.separator + folderPath;
    }

    /**
     * 在远端服务器上下载文件
     *
     * @param remoteDirectory 在远端服务器上要下载的目录 :/data/temp/test/
     * @param remoteFile      在远端服务器上要下载的文件名 :　download.txt
     * @param localDirectory  本地所在文件夹 : D:/test/
     * @param localFile       本地将要生成的的文件名 : download.txt
     * @param sftp            链接
     */
    public static String download(String remoteDirectory, String remoteFile, String localDirectory, String localFile, ChannelSftp sftp) {
        try {
            sftp.cd(remoteDirectory);
            File file = new File(localDirectory);
            // 父级目录
            File parentFile = file.getParentFile();
            if (!parentFile.exists()) {
                parentFile.mkdirs();
            }
            File saveFile = new File(file.getPath(), localFile);
            if (!saveFile.exists()) {
                saveFile.createNewFile();
            }
            log.info("从远端服务器下载文件" + remoteDirectory + File.separator + remoteFile + "到本地" + file.getPath() + File.separator + localFile + " 开始");
            FileOutputStream fileOutputStream = new FileOutputStream(saveFile);
            InputStream inputStream = sftp.get(remoteFile);
            int c;
            byte[] bytes = new byte[1024];
            while ((c = inputStream.read(bytes)) != -1) {
                fileOutputStream.write(bytes, 0, c);
            }
            fileOutputStream.flush();
            inputStream.close();
            fileOutputStream.close();
            log.info("从远端服务器下载文件" + remoteDirectory + File.separator + remoteFile + "到本地" + file.getPath() + File.separator + localFile + " 结束");
            return "0";
        } catch (Exception e) {
            log.error("<<<<<<<<<下载异常>>>>>>>>>>>>");
        }
        return "1";
    }

    /**
     * 在远端服务器上删除文件(仅能删除文件,不能删目录)
     *
     * @param directory  在远端服务器上,要删除文件所在目录 : /data/temp/test/
     * @param deleteFile 在远端服务器上,要删除的文件
     * @param sftp       链接
     */
    public static void delete(String directory, String deleteFile, ChannelSftp sftp) {
        try {
            sftp.cd(directory);
            sftp.rm(deleteFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 在远端服务器上的指定文件夹下创建新的目录(多层次)
     *
     * @param directory  远端服务器上,要创建文件所在目录 : /data/temp/test/
     * @param folderPath 远端服务器上,要创建的文件夹名 : ( 可以为多层次,形如  good 或  test2/good/ok )
     * @param sftp       链接
     */
    public static void mkdir(String directory, String folderPath, ChannelSftp sftp) {
        try {
            sftp.ls(directory);
        } catch (Exception e) {
            try {
                sftp.mkdir(directory);
            } catch (SftpException sftpException) {
                sftpException.printStackTrace();
            }
        }

        try {
            sftp.cd(directory);
        } catch (SftpException sftpException) {
            sftpException.printStackTrace();
        }

        String[] folders;
        if ("\\".equals(File.separator)) {
            folders = folderPath.split("\\\\");
        } else {
            folders = folderPath.split(File.separator);
        }
        for (String currentFolder : folders) {
            try {
                sftp.ls(currentFolder);
                sftp.cd(currentFolder);
            } catch (Exception e) {
                try {
                    sftp.mkdir(currentFolder);
                    sftp.cd(currentFolder);
                } catch (Exception exception) {
                    log.error(currentFolder + " is no exists, make the dir success");
                }
            }
        }
    }

    /**
     * 列出目录下的文件
     *
     * @param directory 要列出的目录
     * @param sftp
     * @return
     * @throws SftpException
     */
    public Vector listFiles(String directory, ChannelSftp sftp)
            throws SftpException {
        return sftp.ls(directory);
    }

    /**
     * 将一个字符串转化为输入流
     */
    public static InputStream getStreamFromString(String sInputString) {
        if (sInputString != null && !sInputString.trim().equals("")) {
            try {
                ByteArrayInputStream tInputStringStream = new ByteArrayInputStream(
                        hexStringToBytes(sInputString));
                return tInputStringStream;
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 16进制字符串转成byte[]数组
     *
     * @param hexString 由PNG图片生成的16进制字符串
     * @return
     */
    public static byte[] hexStringToBytes(String hexString) {
        if (StrUtil.isEmpty(hexString)) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }

    public static ChannelSftp getChannelSftp(String host, int port, String username, String password) {
        SFTPTool sf = new SFTPTool();
        Session session = getSession(host, port, username, password);
        return sf.getConnect(session);
    }

    public static void exec(Session session, String command) {
        ChannelExec channelExec = null;
        try {
            channelExec = (ChannelExec) session.openChannel("exec");
            channelExec.setCommand(command);
            channelExec.connect();
        } catch (Exception e) {
            channelExec = null;
        } finally {
            channelExec.disconnect();
        }
    }

    public static void main(String[] args) throws Exception {
        SFTPTool sf = new SFTPTool();
        Session session = null;
        ChannelSftp channel = null;
        try {
            String host = "192.168.128.25";
            int port = 90;
            String username = "sasftp";
            String password = "Sftp123$%^";
//            session = sf.getSession(host, port, username, password);
//            channel = sf.getConnect(session);

            channel = sftpConnection(host, port, username, password);
//            InputStream stream = SFTPTool.getStreamFromString(streamStr);
            //建目录
//            sf.mkdir    ("/data/temp/test/", "123/888/789", channel);
            //上传
//            upload    (File.separator + "filestore", "E:/xlsx",File.separator + "GIS试验报告.xlsx", channel);
            // 上传
//            String path = "E:\\xlsx\\GIS试验报告.xlsx";
//            upload(File.separator + "filestore", new FileInputStream(path), "GIS试验报告.xlsx", 1, "user", channel);
//            download    (File.separator + "filestore", File.separator + "GIS试验报告.xlsx", "E:/xlsx","GIS试验报告.xlsx", channel);//下载
            delete("/filestore/yzgh/transform/offline/delete/", "变压器.xlsx", channel);//删除
//            channel.cd(File.separator + "filestore");
//            String command = "mv " + (File.separator + "GIS试验报告.xlsx") + " " + (File.separator + "filestore" + File.separator + "image" + File.separator);
//            exec(session, command);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sf.disconnect(channel, session);
        }
    }
}
