package org.jeecg.modules.adweb.common.util;

import ch.ethz.ssh2.ChannelCondition;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 远程Shell执行工具类
 *
 * @author liyl
 * @date 2019-08-13
 */
@Slf4j
public class ShellSSH2Util {

    /** 日志 */
    protected static Logger logger = LoggerFactory.getLogger(ShellSSH2Util.class);

    /** 默认字符编码 */
    private static String DEFAULT_CHART = "UTF-8";

    /**
     * 登录远程服务器
     *
     * @param host 远程主机
     * @param username 远程主机登录用户名
     * @param password 远程主机登录密码
     * @return Connection
     */
    public static Connection login(
            String host, int port, String username, String password, StdoutListener listener) {
        Connection connection = null;
        listener.stdout("开始连接：" + username + "@" + host);
        try {
            connection = new Connection(host, port);
            connection.connect(); // 连接
            boolean login = connection.authenticateWithPassword(username, password); // 认证
            if (login) {
                listener.stdout(username + "@" + host + "； 登录成功");
            } else {
                listener.stderr(username + "@" + host + "； 登录失败");
                connection.close();
            }
        } catch (IOException e) {
            listener.stderr(username + "@" + host + "； 登录失败");
            log.error("服务器host：{}，连接失败：{}", host, e.getMessage());
            connection.close();
        }
        return connection;
    }

    /**
     * 登录远程服务器
     *
     * @param host 远程主机
     * @param username 远程主机登录用户名
     * @param pemFile 登录密钥
     * @param listener 执行过程监听器
     * @return Connection
     */
    public static Connection login(
            String host, int port, String username, File pemFile, StdoutListener listener) {
        Connection connection = null;
        listener.stdout("开始连接：" + username + "@" + host);
        try {
            connection = new Connection(host, port);
            connection.connect(); // 连接
            boolean login = connection.authenticateWithPublicKey(username, pemFile, null); // 认证
            if (login) {
                listener.stdout(username + "@" + host + "； 登录成功");
            } else {
                listener.stderr(username + "@" + host + "； 登录失败");
                connection.close();
            }
        } catch (IOException e) {
            listener.stderr(username + "@" + host + "； 登录失败");
            log.error("服务器host：{}，连接失败：{}", host, e.getMessage());
            connection.close();
        }
        return connection;
    }

    /**
     * 检验是否能够连接服务器
     *
     * @param host 远程主机
     * @param port 端口
     * @param username 远程主机登录用户名
     * @param password 远程主机登录密码
     * @return true：可以连接 false:无法连接
     * @author Cyan -- 2020/4/26 10:56
     */
    public static Boolean loginCheck(
            String host, int port, String username, String password, StdoutListener listener) {
        Boolean result = true;
        Connection connection = null;
        listener.stdout("开始连接：" + username + "@" + host);
        try {
            connection = new Connection(host, port);
            connection.connect(); // 连接
            boolean login = connection.authenticateWithPassword(username, password); // 认证
            if (login) {
                listener.stdout(username + "@" + host + "； 登录成功");
            } else {
                result = false;
                listener.stderr(username + "@" + host + "； 登录失败");
                if (connection != null) {
                    connection.close();
                }
            }
        } catch (IOException e) {
            result = false;
            listener.stderr(username + "@" + host + "； 登录失败");
            if (connection != null) {
                connection.close();
            }
        }
        return result;
    }

    /**
     * 登录远程服务器
     *
     * @param host 远程主机
     * @param username 远程主机登录用户名
     * @param pemFile 登录密钥
     * @param listener 执行过程监听器
     * @return Connection
     */
    private static Connection login(
            String host, String username, File pemFile, StdoutListener listener) {
        Connection connection = null;
        listener.stdout("开始连接：" + username + "@" + host);
        try {
            connection = new Connection(host);
            //            connection.setClient2ServerCiphers(new String[]{"aes256-cbc"});
            connection.connect(); // 连接
            boolean login = connection.authenticateWithPublicKey(username, pemFile, null); // 认证
            if (login) {
                listener.stdout(username + "@" + host + "； 登录成功");
            } else {
                listener.stderr(username + "@" + host + "； 登录失败");
                connection.close();
            }
        } catch (IOException e) {
            listener.stderr(username + "@" + host + "； 登录失败：" + e.getMessage());
            connection.close();
        }
        return connection;
    }

    /**
     * 登录远程服务器
     *
     * @param host 远程主机
     * @param username 远程主机登录用户名
     * @param pemFile 登录密钥
     * @param listener 执行过程监听器
     * @return Connection
     */
    public static Boolean loginCheck(
            String host, String username, File pemFile, StdoutListener listener) {
        Boolean result = true;
        Connection connection = null;
        listener.stdout("开始连接：" + username + "@" + host);
        try {
            connection = new Connection(host);
            connection.connect(); // 连接
            boolean login = connection.authenticateWithPublicKey(username, pemFile, null); // 认证
            if (login) {
                listener.stdout(username + "@" + host + "； 登录成功");
            } else {
                result = false;
                listener.stderr(username + "@" + host + "； 登录失败");
                if (connection != null) {
                    connection.close();
                }
            }
        } catch (IOException e) {
            result = false;
            listener.stderr(username + "@" + host + "； 登录失败");
            if (connection != null) {
                connection.close();
            }
        }
        return result;
    }

    /**
     * 执行远程Shell命令
     *
     * @param host 远程主机地址
     * @param username 远程主机用户名
     * @param pemFilePath 远程主机登录密钥路径
     * @param cmd 执行的命令
     * @param listener 标准输出监听器
     * @return String 执行结果
     */
    public static String execmd(
            String host, String username, String pemFilePath, String cmd, StdoutListener listener) {
        File pemFile = new File(pemFilePath);
        Connection connection = login(host, username, pemFile, listener);
        return execmd(connection, cmd, listener);
    }

    /**
     * 执行远程Shell命令
     *
     * @param host 远程主机地址
     * @param username 远程主机用户名
     * @param password 远程主机密码
     * @param cmd 执行的命令
     * @param listener 标准输出监听器
     * @return String 执行结果
     */
    public static String execmdByPwd(
            String host,
            int port,
            String username,
            String password,
            String cmd,
            StdoutListener listener) {
        Connection connection = login(host, port, username, password, listener);
        return execmd(connection, cmd, listener);
    }

    /**
     * 执行远程Shell命令
     *
     * @param host 远程主机地址
     * @param username 远程主机用户名
     * @param pemFile 远程主机登录密钥
     * @param cmd 执行的命令
     * @param listener 标准输出监听器
     * @return String 执行结果
     */
    public static String execmd(
            String host, String username, File pemFile, String cmd, StdoutListener listener) {
        Connection connection = login(host, username, pemFile, listener);
        return execmd(connection, cmd, listener);
    }

    /**
     * @param host
     * @param port
     * @param username
     * @param pemFile
     * @param cmd
     * @param listener
     * @return
     */
    public static String execmd(
            String host,
            int port,
            String username,
            File pemFile,
            String cmd,
            StdoutListener listener) {
        Connection connection = login(host, port, username, pemFile, listener);
        return execmd(connection, cmd, listener);
    }

    /**
     * 远程执行shll脚本或者命令
     *
     * @param cmd 即将执行的命令
     * @return 命令执行完后返回的结果值
     */
    private static String execmd(Connection connection, String cmd, StdoutListener listener) {
        String result = "";
        Session session = null;
        try {
            if (connection != null) {
                session = connection.openSession(); // 打开一个会话
                session.execCommand(cmd); // 执行命令

                // 使用线程池来管理输出读取线程
                ExecutorService executorService = Executors.newFixedThreadPool(2);

                // 读取标准输出
                Session finalSession = session;
                executorService.submit(
                        () -> {
                            String stdoutResult =
                                    processStdout(
                                            finalSession.getStdout(), DEFAULT_CHART, listener);
                            listener.stdout("命令标准输出结果：\n");
                            listener.stdout(stdoutResult);
                            if (StringUtils.isBlank(stdoutResult)) {
                                listener.stderr("得到标准输出为空, 链接conn:" + connection + ",执行的命令：" + cmd);

                                listener.stderr(
                                        "得到标准输出为空, 链接conn:"
                                                + connection
                                                + ",执行的命令："
                                                + cmd
                                                + "; 错误信息："
                                                + stdoutResult);
                            } else {
                                listener.stdout("执行命令成功, 链接conn:" + connection + ",执行的命令：" + cmd);
                            }
                        });

                // 读取标准错误
                Session finalSession1 = session;
                executorService.submit(
                        () -> {
                            processStdout(finalSession1.getStderr(), DEFAULT_CHART, listener);
                        });

                // 等待所有输出读取线程完成
                executorService.shutdown();
                while (!executorService.isTerminated()) {
                    // 可以在这里设置一个超时，防止无限等待
                    Thread.sleep(300);
                }

                // 等待脚本执行完毕
                session.waitForCondition(ChannelCondition.CLOSED | ChannelCondition.EXIT_STATUS, 0);
            }
        } catch (IOException e) {
            listener.stderr("执行命令失败,链接conn:" + connection + ",执行的命令：" + cmd + " " + e);
            log.error("执行命令失败,链接conn:{},执行的命令：{} {}", connection, cmd, e.getMessage());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            // 关闭会话和连接
            if (session != null) {
                session.close();
            }
            if (connection != null) {
                connection.close();
            }
        }
        return result;
    }

    /**
     * 解析脚本执行返回的结果集
     *
     * @param in 输入流对象
     * @param charset 编码
     * @return 以纯文本的格式返回
     */
    private static String processStdout(InputStream in, String charset, StdoutListener listener) {
        InputStream stdout = new StreamGobbler(in);
        StringBuffer buffer = new StringBuffer();

        try (BufferedReader br = new BufferedReader(new InputStreamReader(stdout, charset))) {
            String line;
            StringBuilder bufferBuilder = new StringBuilder();
            while ((line = br.readLine()) != null) {
                bufferBuilder.append(line).append("\n");
                try {
                    listener.stdout(line);
                } catch (Exception e) {
                    // 记录异常日志或进行其他处理
                    log.error("Error in listener.stdout: {}", e.getMessage());
                }
            }
            buffer.append(bufferBuilder);
        } catch (IOException e) {
            // 记录异常日志或进行其他处理
            buffer.append("解析脚本出错：").append(e.getMessage()).append("\n");
            listener.stderr("解析脚本出错：" + e.getMessage());
            log.error("解析脚本出错：{}", e.getMessage());
        }
        return buffer.toString();
    }

    public static void main(String[] args) {
        long currentTimeMillis = System.currentTimeMillis();
        String host = "52.33.206.188";
        String username = "ubuntu";
        File pemFile = new File("/Users/liyl/Desktop/jumpserver.pem");
        String cmd = "./sb abc T000 test37.advichcloud.com";
        cmd =
                "mysql -h advich-wp-product-db.cqhn3c5h2mcz.us-west-2.rds.amazonaws.com -u admin -p Ew7MEm1EmJpDVvdK";
        //        cmd = "uname -a";
        execmd(
                host,
                username,
                pemFile,
                cmd,
                new StdoutListener() {
                    @Override
                    public void stdout(String line) {
                        System.out.println("标准：" + line);
                        if ("ERROR:FILE_NAME IS EXISTS".equals(line)) {
                            // TODO 已存在，退出
                        }
                    }

                    @Override
                    public void stderr(String line) {
                        System.out.println("错误：" + line);
                    }
                });
        long currentTimeMillis1 = System.currentTimeMillis();
        System.out.println("花费时间：" + (currentTimeMillis1 - currentTimeMillis));
    }

    /**
     * 标准输出监听器
     *
     * @author liyl
     * @date 2019-08-13
     */
    public interface StdoutListener {

        /**
         * 控制台标准输出
         *
         * @param line 每行输出内容
         */
        void stdout(String line);

        /**
         * 控制台错误输出
         *
         * @param line 每行输出内容
         */
        void stderr(String line);
    }
}
