package net.northking.cctp.element.ftp.keywords;


import cn.hutool.core.io.IoUtil;
import cn.hutool.extra.ssh.JschUtil;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import net.northking.cctp.element.core.IExecuteContext;
import net.northking.cctp.element.core.annotation.*;
import net.northking.cctp.element.core.exception.ExecuteException;
import net.northking.cctp.element.core.type.DataType;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.regex.Pattern;

@Keywords
public class FtpTools {

    /**
     * 日志
     */
    private final static Logger logger = LoggerFactory.getLogger(FtpTools.class);

    private final static String SCRIPT_VAR_REGEX = "\\$\\{([^}]*)\\}";

    private final static Pattern SCRIPT_VAR_PATTERN = Pattern.compile(SCRIPT_VAR_REGEX);

    public static boolean containScriptVar(String str){
        return SCRIPT_VAR_PATTERN.matcher(str).find();
    }
    /**
     * <p>执行ftp文件上传到服务器</p>
     * Example:
     *
     * @param host 主机
     * @param port 端口号
     * @param user 用户名
     * @param password 密码
     * @param fileInfo 文件信息
     * @param hostPath 服务器上传地址
     * @param isCreatePath 是否创建路径
     * @param executeContext 上下文对象
     * @return hash表
     */
    @Keyword(alias = "FTP文件上传",category = "0",attributes = "-1")
    public static void ftpUploadFile(
            @Argument(name = "host", comment = "主机", type = DataType.STRING) String host,
            @Argument(name = "port", comment = "端口号", type = DataType.INTEGER) Integer port,
            @Argument(name = "user", comment = "用户名", type = DataType.STRING) String user,
            @Argument(name = "password", comment = "密码", type = DataType.STRING) String password,
            @Argument(name = "fileInfo", comment = "文件id", type = DataType.LIST) List<Map<String,String>> fileInfo,
            @Argument(name = "hostPath", comment = "服务器上传地址", type = DataType.STRING) String hostPath,
            @Argument(name = "isCreatePath", comment = "是否创建路径", type = DataType.BOOLEAN) Boolean isCreatePath,
            IExecuteContext executeContext)
    {

        InputStream is = null;
        Session session = null;
        try {
            logger.info("开始");
            session = JschUtil.getSession(host,port,user,password);
            Properties config = new Properties();
            config.put("StrictHostKeyChecking","no");
            session.setConfig(config);

            Channel channel = session.openChannel("sftp");
            channel.connect();

            ChannelSftp sftp = (ChannelSftp) channel;
            if(mkdirPath(sftp,hostPath,isCreatePath)){
                logger.info("进入路径："+hostPath);
                //获取文件流
                for (Map<String,String> fileOne : fileInfo) {
                    String fileName = fileOne.get("fileName");
                    String fileId = fileOne.get("fileId");
                    is = getFileByFileId(fileId, executeContext.getProperty("atu.server.serverUrl")+executeContext.getProperty("atu.server.fileDownloadPath"));
                    //文件流上传服务器
                    sftp.put(is,fileName,ChannelSftp.OVERWRITE);
                }
            }else {
                throw new RuntimeException("服务器路径进入失败");
            }

        }catch (Exception e){
            logger.error("ftp报错",e);
            throw new RuntimeException("文件上传失败:"+e.getMessage());
        }finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    logger.error("ftp报错",e);
                }
            }
            if (session != null) {
                session.disconnect();
                JschUtil.close(session);
            }
        }

    }


    private static boolean  mkdirPath(ChannelSftp sftp,String hostPath,Boolean isCreatePath){
        String[] paths = hostPath.replaceFirst("/","").split("/");
        boolean result = true;
        int i=0;
        for (String path : paths) {
            //校验路径是否合法
            if (containScriptVar(path)) {
                logger.error(path + "---->文件路径不合法");
                throw new ExecuteException(path + "文件路径不合法");
            }
            try {
                if(i==0){
                    path = "/"+path;
                }
                sftp.cd(path);
                i++;
            } catch (SftpException e) {
                if(isCreatePath){
                    logger.info("创建文件夹:"+path);
                    try {
                        sftp.mkdir(path);
                        sftp.cd(path);
                    } catch (SftpException e1) {
                        logger.error("创建文件夹["+path+"]失败:",e);
                        result=false;
                    }
                }else{
                    result=false;
                }
            }
        }
        return result;
    }

    /**
     * 获取文件流
     * @param fileId
     * @return
     */
    private static InputStream getFileByFileId(String fileId,String downloadPath) {
        String fileDown = downloadPath+fileId;
        InputStream inputStream = null;
        try {
            SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
            factory.setConnectTimeout(15000);
            factory.setReadTimeout(15000);
            RestTemplate template = new RestTemplate(factory);
            ResponseEntity<byte[]> resultEntity = null;
            byte[] body = null;
            try {
                resultEntity = template.getForEntity(fileDown, byte[].class);
            } catch (HttpClientErrorException e) {
                if (HttpStatus.NOT_FOUND == e.getStatusCode()) {
                    throw new ExecuteException("下载失败", e);
                }
                throw new ExecuteException("下载失败", e);
            }
            if (resultEntity.getBody() == null) {
                body = new byte[0];
            } else {
                body = resultEntity.getBody();
            }
            inputStream = IoUtil.toStream(body);
        } catch (Exception e) {
            logger.error("获取文件失败，失败原因：", e);
            throw new RuntimeException("获取文件失败");
        }
        return inputStream;
    }
}