package com.bcxin.signature.util.ftp;

import com.bcxin.signature.config.FileModeConfig;
import lombok.extern.slf4j.Slf4j;
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 java.io.*;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

/**
 * <b> ftp客户端 </b>
 * @author ZXF
 * @create 2023/07/07 0007 13:51
 * @version
 * @注意事项 </b>
 */
@Slf4j
public class FtpConnection {

    private FTPClient ftp = new FTPClient();

    private boolean is_connected = false;

    /**
     * 构造函数
     */
    public FtpConnection() {
        is_connected = false;
        ftp.setDefaultTimeout(FileModeConfig.defaultTimeoutSecond * 1000);
        ftp.setConnectTimeout(FileModeConfig.connectTimeoutSecond * 1000);
        ftp.setDataTimeout(FileModeConfig.dataTimeoutSecond * 1000);
        try {
            initConnect(FileModeConfig.getHost(), FileModeConfig.getPort(), FileModeConfig.getUserName(), FileModeConfig.getPassword());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 初始化连接
     *
     * @param host
     * @param port
     * @param user
     * @param password
     * @throws IOException
     */
    private void initConnect(String host, int port, String user, String password) throws IOException {
        try {
            if(!ftp.isConnected()){
                ftp.connect(host, port);
            }
        } catch (UnknownHostException ex) {
            throw new IOException("Can't find FTP server '" + host + "'");
        }
        //被动模式
        ftp.enterLocalPassiveMode();
        int reply = ftp.getReplyCode();//220 连接成功
        if (!FTPReply.isPositiveCompletion(reply)) {
            disconnect();
            throw new IOException("Can't connect to server '" + host + "'");

        }
        if (!ftp.login(user, password)) {
            is_connected = false;
            disconnect();
            throw new IOException("Can't login to server '" + host + "'");
        } else {
            is_connected = true;
        }
    }

    /**
     * 上传文件
     *
     * @param path
     * @param ftpFileName
     * @param localFile
     * @throws IOException
     */
    public boolean upload(String path, String ftpFileName, File localFile) throws IOException {
        boolean is  = false;
        //检查本地文件是否存在
        if (!localFile.exists()) {
            throw new IOException("Can't upload '" + localFile.getAbsolutePath() + "'. This file doesn't exist.");
        }

        // 转换路径和文件名，防止设置工作路径出错
        conv(path,ftpFileName);

        if(hasChineseUsingRegex(ftpFileName)){
            return is;
        }
        if(StringUtils.isNotEmpty(path)){
            path = new String(path.getBytes("GBK"), "iso-8859-1");
            //设置工作路径
            setWorkingDirectory(path);
        }
        ftpFileName = new String(ftpFileName.getBytes("GBK"), "iso-8859-1");
        //上传
        try(InputStream in = new BufferedInputStream(new FileInputStream(localFile));) {
            String filePath = path + "/" + ftpFileName;
            filePath = filePath.replace("\\", "/");
            ftp.setFileType(FTP.BINARY_FILE_TYPE);
            //保存文件
            is = ftp.storeFile(filePath, in);
        }catch (Exception e){
            e.printStackTrace();
        }
        return is;
    }

    public boolean uploadBase64(String path, String ftpFileName, String base64Content) throws IOException {
        boolean is = false;
        // 检查Base64内容是否为空
        if (base64Content == null || base64Content.isEmpty()) {
            throw new IOException("Can't upload. The base64 content is empty.");
        }
        // 转换路径和文件名，防止设置工作路径出错
        conv(path,ftpFileName);
        if(hasChineseUsingRegex(ftpFileName)){
            return is;
        }
        if(StringUtils.isNotEmpty(path)){
            path = new String(path.getBytes("GBK"), "iso-8859-1");
            // 设置工作路径
            setWorkingDirectory(path);
        }
        ftpFileName = new String(ftpFileName.getBytes("GBK"), "iso-8859-1");


        // 将Base64字符串解码为字节数组
//        byte[] decodedBytes = Base64.getDecoder().decode(base64Content);
        byte[] decodedBytes = base64Content.getBytes();

        // 上传
        try (InputStream in = new ByteArrayInputStream(decodedBytes)) {
            String filePath = path + "/" + ftpFileName;
            filePath = filePath.replace("\\", "/");
            ftp.setFileType(FTP.BINARY_FILE_TYPE);
            // 保存文件
            is = ftp.storeFile(filePath, in);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return is;
    }

    private static void conv(String path,String ftpFileName){
        ftpFileName = ftpFileName.startsWith("/")?ftpFileName:"/"+ftpFileName;
        Path sourcePath = Paths.get(path+ftpFileName);
        if(sourcePath.getParent() !=null){
            path = sourcePath.getParent().toAbsolutePath().toString();
        }
        ftpFileName = sourcePath.getFileName().toString();
    }

    public static boolean hasChineseUsingRegex(String str) {
        return str.matches(".*[\u4e00-\u9fa5]+.*");
    }

    /**
     * 剪切 pathA 目录下的文件到 pathB 目录下，并替换文件内容为 JSON 字符串
     *
     * @param pathA      源目录
     * @param pathB      目标目录
     * @param fileName   文件名
     * @param jsonContent JSON 字符串内容
     * @throws IOException
     */
    public boolean moveAndReplaceFile(String pathA, String pathB, String fileName, String jsonContent) throws IOException {
        boolean success = false;

        // 检查 JSON 内容是否为空
        if (jsonContent == null || jsonContent.isEmpty()) {
            throw new IOException("Can't replace file content. The JSON content is empty.");
        }

        if(hasChineseUsingRegex(fileName)){
            return success;
        }
        pathA = new String(pathA.getBytes("GBK"), "iso-8859-1");
        pathB = new String(pathB.getBytes("GBK"), "iso-8859-1");
        fileName = new String(fileName.getBytes("GBK"), "iso-8859-1");

        // 设置源目录
        setWorkingDirectory(pathA);

        // 构建源文件路径
        String sourceFilePath = pathA + "/" + fileName;
        sourceFilePath = sourceFilePath.replace("\\", "/");

        // 检查源文件是否存在
        if (!fileExists(sourceFilePath)) {
            throw new IOException("Source file does not exist: " + sourceFilePath);
        }

        // 设置目标目录
        setWorkingDirectory(pathB);

        // 构建目标文件路径
        String targetFilePath = pathB + "/" + fileName;
        targetFilePath = targetFilePath.replace("\\", "/");

        // 移动文件
        success = ftp.rename(sourceFilePath, targetFilePath);
        if (!success) {
            throw new IOException("Failed to move file from " + sourceFilePath + " to " + targetFilePath);
        }

        // 将 JSON 字符串转换为字节数组
        byte[] jsonBytes = jsonContent.getBytes(StandardCharsets.UTF_8);

        // 上传新的文件内容
        try (InputStream in = new ByteArrayInputStream(jsonBytes)) {
            ftp.setFileType(FTP.BINARY_FILE_TYPE);
            success = ftp.storeFile(targetFilePath, in);
        } catch (Exception e) {
            e.printStackTrace();
            throw new IOException("Failed to replace file content: " + e.getMessage());
        }

        return success;
    }

    /**
     * 检查 FTP 服务器上的文件是否存在
     *
     * @param filePath 文件路径
     * @return 文件存在返回 true，否则返回 false
     * @throws IOException
     */
    private boolean fileExists(String filePath) throws IOException {
        // 获取文件的父目录
        String parentDir = new java.io.File(filePath).getParent();
        // 获取文件名
        String fileName = new java.io.File(filePath).getName();

        // 获取文件列表
        FTPFile[] files = ftp.listFiles();

        // 检查文件列表中是否存在指定的文件
        for (FTPFile file : files) {
            if (file.getName().equals(fileName)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 下载文件
     *
     * @param path ftp操作目录
     * @param ftpFileName ftp文件名称
     * @param localFile 本地文件地址
     * @throws IOException
     */
    public boolean download(String path, String ftpFileName, File localFile) throws IOException {
        boolean is = false;
        // 检查本地文件是否存在
        if (localFile.exists()) {
            throw new IOException("Can't download '" + localFile.getAbsolutePath() + "'. This file already exists.");
        }

        // 转换路径和文件名，防止设置工作路径出错
        conv(path,ftpFileName);

        if(hasChineseUsingRegex(ftpFileName)){
            return is;
        }
        System.err.println("=======>获取ftp目录下指定文件转存,ftp路径："+path+",文件名："+ftpFileName+"<=======");
        if(StringUtils.isNotEmpty(path)){
            path = new String(path.getBytes("GBK"), "iso-8859-1");
            System.err.println("=======>获取ftp目录下指定文件转存,节点：101<=======");
            // 设置工作路径
            setWorkingDirectory(path);
        }
        ftpFileName = new String(ftpFileName.getBytes("GBK"), "iso-8859-1");
        System.err.println("=======>获取ftp目录下指定文件转存,节点：102<=======");
        // 下载
        try (OutputStream out = new BufferedOutputStream(new FileOutputStream(localFile))) {
            String filePath = path + "/" + ftpFileName;
            filePath = filePath.replace("\\", "/");
            System.err.println("=======>获取ftp目录下指定文件,实际路径："+filePath+"<=======");
            ftp.setFileType(FTP.BINARY_FILE_TYPE);
            // 保存文件
            is = ftp.retrieveFile(filePath, out);
        } catch (Exception e) {
            System.err.println("=======>获取ftp目录下指定文件转存,节点：103<=======");
            e.printStackTrace();
        }
        System.err.println("=======>获取ftp目录下指定文件转存,节点：104<=======");
        return is;
    }

    /**
     * 下载文件并返回Base64编码的字符串
     *
     * @param path ftp操作目录
     * @param ftpFileName ftp文件名称
     * @return Base64编码的字符串
     * @throws IOException
     */
    public String downloadAsBase64(String path, String ftpFileName) throws IOException {

        // 转换路径和文件名，防止设置工作路径出错
        conv(path,ftpFileName);

        if(hasChineseUsingRegex(ftpFileName)){
            return "";
        }
        if(StringUtils.isNotEmpty(path)){
            path = new String(path.getBytes("GBK"), "iso-8859-1");
            // 设置工作路径
            setWorkingDirectory(path);
        }
        ftpFileName = new String(ftpFileName.getBytes("GBK"), "iso-8859-1");
        // 下载
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();OutputStream out = new BufferedOutputStream(baos)) {
            String filePath = path + "/" + ftpFileName;
            filePath = filePath.replace("\\", "/");
            ftp.setFileType(FTP.BINARY_FILE_TYPE);
            // 保存文件
            boolean is = ftp.retrieveFile(filePath, out);
            if (!is) {
                throw new IOException("Failed to retrieve file: " + filePath);
            }

            // 将字节数组转换为Base64编码的字符串
            byte[] fileBytes = baos.toByteArray();
            return Base64.getEncoder().encodeToString(fileBytes);
        } catch (Exception e) {
            e.printStackTrace();
            throw new IOException("Error while downloading file: " + e.getMessage());
        }

    }

    /**
     * 获取ftp文件内容字符串
     *
     * @param path ftp操作目录
     * @param ftpFileName ftp文件名称
     * @throws IOException
     */
    public String getFileContent(String path, String ftpFileName) throws IOException {

        // 转换路径和文件名，防止设置工作路径出错
        conv(path,ftpFileName);

        if(StringUtils.isNotEmpty(path)){
            path = new String(path.getBytes("GBK"), "iso-8859-1");
            // 设置工作路径
            setWorkingDirectory(path);
        }
        ftpFileName = new String(ftpFileName.getBytes("GBK"), "iso-8859-1");
        // 读取文件内容
        StringBuilder content = new StringBuilder();
        ftpFileName = ftpFileName.startsWith("/")?ftpFileName:"/"+ftpFileName;

        if(hasChineseUsingRegex(ftpFileName)){
            return "";
        }
        try (InputStream in = new BufferedInputStream(ftp.retrieveFileStream(path + ftpFileName))) {
            if (in == null) {
                return ""; // 文件未找到
            }
            byte[] buffer = new byte[1024];
            int length;
            while ((length = in.read(buffer)) != -1) {
                content.append(new String(buffer, 0, length, "UTF-8"));
            }
//            ftp.completePendingCommand(); // 确保命令完成
        } catch (Exception e) {
            if(e.getMessage().contains("Stream closed")){
                System.err.println("=======>获取ftp文件内容字符串(文件不存在 Stream closed).getFileContent.ftpFileName："+ftpFileName+"  <=======");
            }
//            e.printStackTrace();
            return ""; // 发生异常
        }
        return content.toString();
    }

    /**
     * 获取ftp目录下所有最近5分钟内生成的文件的文件名，返回文件名列表
     *
     * @param path ftp操作目录
     * @return 最近5分钟内生成的文件名列表
     * @throws IOException
     */
    public List<String> fileNames(String path,String keywork) throws IOException {
        if(StringUtils.isEmpty(keywork)){
            keywork = "IN-";
        }
        System.err.println("=======>获取ftp目录下所有文件业务,ftp路径："+path+",关键字："+keywork+"<=======");
        if(StringUtils.isNotEmpty(path)){
            System.err.println("=======>获取ftp目录下所有文件业务节点：001.1<=======");
            path = new String(path.getBytes("GBK"), "iso-8859-1");
            // 设置工作路径
            setWorkingDirectory(path);
            System.err.println("=======>获取ftp目录下所有文件业务节点：001<=======");
        }
        System.err.println("=======>获取ftp目录下所有文件业务节点：002<=======");

        List<String> recentFiles = new ArrayList<>();
        FTPFile[] files = ftp.listFiles();
        System.err.println("=======>获取ftp目录下所有文件业务节点：003<=======");

        long currentTime = System.currentTimeMillis();
        long fiveMinutesAgo = currentTime - (5 * 60 * 1000); // 5 minutes ago in milliseconds

        System.err.println("=======>获取ftp目录下所有文件的数量："+files.length+"<=======");
        for (FTPFile file : files) {
            System.err.println("=======>获取ftp目录下所有文件的名称.filename："+file.getName()+"<=======");
            //转utc 时间加8小时
            Long fileTime = file.getTimestamp().getTimeInMillis()+file.getTimestamp().getTimeZone().getOffset(0);
            if (file.isFile() && fileTime >= fiveMinutesAgo && file.getName().startsWith(keywork)) {
                System.err.println("=======>获取ftp目录下所有文件业务有效文件名："+file.getName()+"<=======");
                recentFiles.add(file.getName());
            }
        }
        System.err.println("=======>获取ftp目录下所有最近5分钟内生成的文件的数量："+recentFiles.size()+"<=======");

        return recentFiles;
    }

    public boolean delectFile(String path,String filename) throws IOException {
        if(StringUtils.isNotEmpty(path)){
            path = new String(path.getBytes("GBK"), "iso-8859-1");
            // 设置工作路径
            setWorkingDirectory(path);
        }
        return ftp.deleteFile(filename);
    }

    /**
     * 关闭连接
     *
     * @throws IOException
     */
    public void disconnect() throws IOException {
        if (ftp.isConnected()) {
            try {
                ftp.logout();
                ftp.disconnect();
                is_connected = false;
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * 设置工作路径
     *
     * @param dir
     * @return
     */
    private boolean setWorkingDirectory(String dir) {
        if (!is_connected) {
            return false;
        }
        //如果目录不存在创建目录
        try {
            if (createDirecroty(dir)) {
                return ftp.changeWorkingDirectory(dir);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;

    }

    /**
     * 是否连接
     *
     * @return
     */
    public boolean isConnected() {
        return is_connected;
    }

    /**
     * 创建目录
     *
     * @param remote
     * @return
     * @throws IOException
     */
    private boolean createDirecroty(String remote) throws IOException {
        if(StringUtils.isEmpty(remote)){
            return false;
        }
        boolean success = true;
        String directory = remote.substring(0, remote.lastIndexOf("/") + 1);
        // 如果远程目录不存在，则递归创建远程服务器目录
        if (!directory.equalsIgnoreCase("/") && !ftp.changeWorkingDirectory(new String(directory))) {
            int start = 0;
            int end = 0;
            if (directory.startsWith("/")) {
                start = 1;
            } else {
                start = 0;
            }
            end = directory.indexOf("/", start);
            while (true) {
                String subDirectory = new String(remote.substring(start, end));
                if (!ftp.changeWorkingDirectory(subDirectory)) {
                    if (ftp.makeDirectory(subDirectory)) {
                        ftp.changeWorkingDirectory(subDirectory);
                    } else {
                        log.error("mack directory error :/" + subDirectory);
                        return false;
                    }
                }
                start = end + 1;
                end = directory.indexOf("/", start);
                // 检查所有目录是否创建完毕
                if (end <= start) {
                    break;
                }
            }
        }
        return success;
    }
}
