package nckd.base.helper;

import kd.bos.cache.CacheFactory;
import kd.bos.cache.TempFileCache;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.fileservice.FileService;
import kd.bos.fileservice.FileServiceFactory;
import kd.bos.fileservice.utils.FileTimeoutUtil;
import kd.bos.logging.Log;
import kd.bos.logging.LogFactory;
import kd.bos.orm.query.QCP;
import kd.bos.orm.query.QFilter;
import kd.bos.servicehelper.AttachmentDto;
import kd.bos.servicehelper.AttachmentServiceHelper;
import kd.bos.servicehelper.BusinessDataServiceHelper;
import kd.bos.util.FileUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.springframework.util.StreamUtils;

import javax.net.ssl.SSLContext;

import java.io.*;

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.DecimalFormat;
import java.util.*;


public class FileSECUtils {
    private static final Log logger = LogFactory.getLog(FileSECUtils.class);


    /**
     * 检查文件是否加密的REST接口调用方法
     * 该方法会从配置中获取检查URL，构造请求并发送文件流进行检测。
     * 返回结果说明：
     *   1: 文件是秘文（加密状态）
     *   0: 文件是明文（未加密状态）
     *  -1: 发生错误或未找到配置信息
     */
    public static int checkFileIsEncryptionRest(InputStream inputStream){

        Map<String, String> mapentity = CommonHelperUtils.getCommonParams("SEC");
        if(mapentity == null){
            return -1;
        }
        String url = mapentity.get("url");

        try {
            CloseableHttpClient httpclient = null;
            HttpPost post = new HttpPost(url);
            post.addHeader("method~name", "checkFileIsEncryptionRest");//文件加密

            //String filePath = "C:\\Users\\Administrator\\Desktop\\test.txt";
            //InputStream inputStream = new FileInputStream(filePath);

            AbstractHttpEntity entity = new InputStreamEntity(inputStream);
            post.setEntity(entity);

            logger.info("--------------SEC附件检查文件 buildSSLCloseableHttpClient() ----------------");
            httpclient = getHttpClient();
            logger.info("--------------SEC附件检查文件 httpclient.execute(post) ----------------");
            CloseableHttpResponse response = httpclient.execute(post);
            String result = response.getFirstHeader("data~returnFlag").getValue();
            logger.info("--------------SEC附件检查文件返回 "+result+" ----------------");

            // 获取响应状态码
            if("1".equals(result)){//1表示秘文
                System.out.println("是秘文");
                return 1;
            }else if("0".equals(result)){//0表示明文
                System.out.println("是明文");
                return 0;
            }else{
                System.out.println(result);
                return -1;
            }

        } catch (IOException e) {
            e.printStackTrace();
            logger.info("SEC附件检查是否明文 ERR content1: " + e.getMessage());
        } catch (Exception e1){
            e1.printStackTrace();
            logger.info("SEC附件检查是否明文 ERR content2: " + e1.getMessage());
        }
        return -1;
    }

    public static InputStream encodeFileForSEC(long fileSize,InputStream inputStream){

        Map<String, String> mapentity = CommonHelperUtils.getCommonParams("SEC");
        if(mapentity == null){
            return null;
        }
        String url = mapentity.get("url");

        try {

            //String filePath = "C:\\Users\\turbo_i49t4d3\\Downloads\\副本经理人周会数据.xlsx";
            //File file = new File(filePath);
            //InputStream inputStream1 = new FileInputStream(filePath);

            CloseableHttpClient httpclient = null;
            HttpPost post = new HttpPost(url);
            post.addHeader("method~name", "fileEncryptionRest"); //文件加密
            post.addHeader("data~fileOffset", "0");
            post.addHeader("data~counSize", String.valueOf(fileSize));
            post.addHeader("data~secLevel", "10");

            AbstractHttpEntity entity = new InputStreamEntity(inputStream);
            post.setEntity(entity);

            httpclient = getHttpClient();
            logger.info("--------------SEC附件加密 httpclient.execute(post) ----------------");
            CloseableHttpResponse response = httpclient.execute(post);
            String result = response.getFirstHeader("data~returnFlag").getValue();
            logger.info("--------------SEC附件加密文件返回 "+result+" ----------------");
            // 获取响应状态码
            if("0".equals(result)){//1表示秘文
                InputStream ins = response.getEntity().getContent();
                return ins;
            }else{
                return null;
            }

        } catch (IOException e) {
            e.printStackTrace();
            logger.info("SEC附件加密 ERR content1: " + e.getMessage());
        } catch (Exception e1){
            e1.printStackTrace();
            logger.info("SEC附件加密 ERR content2: " + e1.getMessage());
        }
        return null;
    }

    public static InputStream decodeFileForSEC(long fileSize,InputStream inputStream){
        Map<String, String> mapentity = CommonHelperUtils.getCommonParams("SEC");
        if(mapentity == null){
            return null;
        }
        String url = mapentity.get("url");

        try {

            CloseableHttpClient httpclient = null;
            HttpPost post = new HttpPost(url);
            post.addHeader("method~name", "fileDecryptionRest"); //文件解密
            post.addHeader("data~fileOffset", "0");
            post.addHeader("data~counSize", String.valueOf(fileSize));

            AbstractHttpEntity entity = new InputStreamEntity(inputStream);
            post.setEntity(entity);

            httpclient = getHttpClient();
            logger.info("--------------SEC附件解密 httpclient.execute(post) ----------------");
            CloseableHttpResponse response = httpclient.execute(post);
            String result = response.getFirstHeader("data~returnFlag").getValue();
            logger.info("--------------SEC附件解密返回 "+result+" ----------------");
            // 获取响应状态码
            if("0".equals(result)){//1表示秘文
                InputStream ins = response.getEntity().getContent();
                return ins;
            }else{
                return null;
            }

        } catch (IOException e) {
            e.printStackTrace();
            logger.info("SEC附件解密 ERR content1: " + e.getMessage());
        } catch (Exception e1){
            e1.printStackTrace();
            logger.info("SEC附件解密 ERR content2: " + e1.getMessage());
        }
        return null;
    }

    public static CloseableHttpClient getHttpClient() {
        CloseableHttpClient httpClient = null;
        try {
            TrustStrategy acceptingTrustStrategy = (chain, authType) -> true;
            SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
            logger.info("--------------SEC附件 buildSSLCloseableHttpClient1 ----------------");
            HttpClientBuilder clientBuilder = HttpClients.custom();

            httpClient = clientBuilder.setSSLSocketFactory(sslsf).build();
            logger.info("--------------SEC附件 buildSSLCloseableHttpClient2 ----------------");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return httpClient;
    }

    public static InputStream processFileWithEncodeSEC(String path,InputStream inputStream) {
        File tempFile = null;
        try {
            logger.info("--------------SEC附件加密 processFileWithEncodeSEC ----------------");
            String filename = getFileNameWithoutExtension(path);
            String filetype = getFileType(path);
            /**
             * 判断是否是媒体文件，如果是媒体文件，则不进行处理
             */
            boolean isMedia = isMediaFile(filetype);
            if(isMedia){
                return inputStream;
            }
            String filepath =  System.getProperty("java.io.tmpdir") + "/" + filename;
            // 1. 将 InputStream 写入本地临时文件
            tempFile = File.createTempFile(filepath,"."+filetype);
            logger.info("--------------SEC附件临时路径 "+filepath,"."+filetype+" ----------------");
            tempFile.deleteOnExit(); // 确保 JVM 退出时删除临时文件

            try (FileOutputStream fos = new FileOutputStream(tempFile)) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    fos.write(buffer, 0, bytesRead);
                }
            }
            //返回临时文件输入流，不能使用的文件流
            InputStream tmpInputStream = new FileInputStream(tempFile);

            // 2. 调用是否加密接口方法
            int isEncrypted = checkFileIsEncryptionRest(new FileInputStream(tempFile));
            if (isEncrypted == -1) {
                logger.info("--------------SEC附件检查，文件加密状态检查失败----------------");
                return tmpInputStream;
            } else if (isEncrypted == 1) {
                logger.info("--------------SEC附件检查，文件已加密----------------");
                return tmpInputStream;
            }

            // 3. 获取文件大小
            long fileSize = tempFile.length();

            // 4. 调用加密方法
            InputStream encodeInputStream = encodeFileForSEC(fileSize, new FileInputStream(tempFile));
            if (encodeInputStream == null) {
                logger.info("--------------SEC附件加密，文件加密失败----------------");
                return tmpInputStream;
            }

            // 5. 删除临时文件
            if (!tempFile.delete()) {
                logger.info("--------------SEC附件加密，无法删除临时文件----------------");
            }

            // 返回解密后的输入流
            return encodeInputStream;

        } catch (IOException e) {
            e.printStackTrace();
            logger.info("--------------SEC附件加密 处理过程中发生错误1: " + e.getMessage());
            return null;
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
                logger.info("--------------SEC附件加密 处理过程中发生错误2: " + e.getMessage());
            }
        }
    }

    public static InputStream processFileWithSEC(String path, InputStream inputStream) {
        File tempFile = null;
        try {
            String fileUuid = UUID.randomUUID().toString().replace("-", "");
            String filepath =  System.getProperty("java.io.tmpdir") + "/" + fileUuid;
            // 1. 将 InputStream 写入本地临时文件
            tempFile = File.createTempFile(filepath,"");

            logger.info("--------------SEC附件检查，临时文件路径：" + filepath);

            try (FileOutputStream fos = new FileOutputStream(tempFile)) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    fos.write(buffer, 0, bytesRead);
                }
            }
            //返回临时文件输入流，不能使用的文件流
            InputStream tmpInputStream = new FileInputStream(tempFile);

            // 2. 调用是否加密接口方法
            filepath = System.getProperty("java.io.tmpdir") + "/" + tempFile.getName();

            int isEncrypted = checkFileIsEncryptionRest(new FileInputStream(tempFile));
            if (isEncrypted == -1) {
                logger.info("--------------SEC附件检查，文件加密状态检查失败----------------");

                String tempPath = FileUtils.checkFileUrl(filepath);
                FileTimeoutUtil.removeFile(tempPath, tmpInputStream, 120 * 1000); //  定时 120 秒后删除文件

                return tmpInputStream;
            } else if (isEncrypted == 0) {
                logger.info("--------------SEC附件检查，是明文无需解密----------------");

                String tempPath = FileUtils.checkFileUrl(filepath);
                FileTimeoutUtil.removeFile(tempPath, tmpInputStream, 120 * 1000); //  定时 120 秒后删除文件

                return tmpInputStream;
            }

            // 3. 获取文件大小
            long fileSize = tempFile.length();

            // 4. 调用解密方法
            InputStream decryptedInputStream = decodeFileForSEC(fileSize, new FileInputStream(tempFile));
            if (decryptedInputStream == null) {
                logger.info("--------------SEC附件解密，文件解密失败----------------");
                return tmpInputStream;
            }

            // 5. 删除临时文件
            if (!tempFile.delete()) {
                logger.info("--------------SEC附件加密，无法删除临时文件----------------");
            }

            // 返回解密后的输入流
            return decryptedInputStream;

        } catch (IOException e) {
            //e.printStackTrace();
            logger.info("--------------SEC附件解密 处理过程中发生错误1: " + e.getMessage());
            return null;
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
                logger.info("--------------SEC附件解密 处理过程中发生错误2: " + e.getMessage());
            }
        }
    }

    public static String getFileType(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            logger.info("--------------SEC附件 文件名为空时返回------" );
            return null; // 文件名为空时返回 null
        }

        int lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex == -1 || lastDotIndex == fileName.length() - 1) {
            logger.info("--------------SEC附件 文件名为空时返回------" );
            return null; // 没有扩展名或文件名以点结尾时返回 null

        }
        return fileName.substring(lastDotIndex + 1); // 返回文件扩展名
    }

    public static String getFileNameWithoutExtension(String filePath) {
        // 提取最后一段文件名
        int lastSlashIndex = filePath.lastIndexOf('/');
        String fileName = (lastSlashIndex == -1) ? filePath : filePath.substring(lastSlashIndex + 1);

        int dotIndex = fileName.lastIndexOf('.');
        if (dotIndex == -1) {
            return fileName;
        }
        return fileName.substring(0, dotIndex);
    }

    public static String getFileNameWithoutExtensionWindows(String filePath) {
        // 提取最后一段文件名
        int lastSlashIndex = filePath.lastIndexOf('\\');
        String fileName = (lastSlashIndex == -1) ? filePath : filePath.substring(lastSlashIndex + 1);

        int dotIndex = fileName.lastIndexOf('.');
        if (dotIndex == -1) {
            return fileName;
        }
        return fileName.substring(0, dotIndex);
    }

    public static boolean isMediaFile(String fileType) {
        if (fileType == null || fileType.isEmpty()) {
            return false;
        }

        // 转换为小写，便于匹配
        String type = fileType.toLowerCase();

        List<String> mediaTypes = Arrays.asList(
                "mp4", "mkv", "flv", "avi", "mov", "wmv",
                "mpeg", "mpg", "3gp", "webm", "ts", "m4v",
                "jpg", "jpeg", "png", "gif", "bmp", "webp",
                "tiff", "svg", "ico", "heic","pic"
        );

        return mediaTypes.contains(type);
    }


    public static List<String> getDownFileUrl(List<Map<String, Object>> fileList){
        List<String> downFileUrl = new ArrayList<>();
        for (Map map : fileList) {
            String url = map.get("url").toString();//获取到url

            FileService fileService = FileServiceFactory.getAttachmentFileService();
            String path = fileService.getFileServiceExt().getRealPath(url);
            InputStream inputStream = fileService.getInputStream(url);
            getFileUrlByEncodeSEC(path,inputStream);
        }
        return downFileUrl;
    }

    public static String getFileUrlByEncodeSEC(String path,InputStream inputStream) {
        File tempFile = null;
        try {
            logger.info("--------------SEC附件加密 processFileWithSEC1 ----------------");
            String filename = getFileNameWithoutExtension(path);
            String filetype = getFileType(path);
            /**
             * 判断是否是媒体文件，如果是媒体文件，则不进行处理
             */
            boolean isMedia = isMediaFile(filetype);
            if(isMedia){
                return "";
            }
            String filepath =  System.getProperty("java.io.tmpdir") + "/" + filename;
            //String filepath =  System.getProperty("java.io.tmpdir") + "\\" + filename;
            // 1. 将 InputStream 写入本地临时文件
            tempFile = File.createTempFile(filepath,"."+filetype);
            logger.info("--------------SEC附件临时路径 " + filepath + "." + filetype + " ----------------");
            tempFile.deleteOnExit(); // 确保 JVM 退出时删除临时文件

            try (FileOutputStream fos = new FileOutputStream(tempFile)) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    fos.write(buffer, 0, bytesRead);
                }
            }
            //返回临时文件输入流，不能使用的文件流
            //InputStream tmpInputStream = new FileInputStream(tempFile);

            // 2. 调用是否加密接口方法
            int isEncrypted = checkFileIsEncryptionRest(new FileInputStream(tempFile));
            if (isEncrypted == -1) {
                logger.info("--------------SEC附件检查，文件加密状态检查失败----------------");
                return "";
            } else if (isEncrypted == 1) {
                logger.info("--------------SEC附件检查，是明文无需解密----------------");
                return "";
            }

            // 3. 获取文件大小
            long fileSize = tempFile.length();

            // 4. 调用加密方法
            InputStream decryptedInputStream = encodeFileForSEC(fileSize, new FileInputStream(tempFile));
            if (decryptedInputStream == null) {
                logger.info("--------------SEC附件加密，文件解密失败----------------");
                return "";
            }

            // 5. 删除临时文件
            if (!tempFile.delete()) {
                logger.info("--------------SEC附件加密，无法删除临时文件----------------");
            }

            String url = getDownloadUrl(filename+"."+filetype, decryptedInputStream);

            // 返回解密后的输入流
            return url;

        } catch (IOException e) {
            e.printStackTrace();
            logger.info("--------------SEC附件加密 处理过程中发生错误1: " + e.getMessage());
            return null;
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
                logger.info("--------------SEC附件加密 处理过程中发生错误2: " + e.getMessage());
            }
        }
    }

    /**
     * 下载文件
     *
     * @param fileName 要下载的文件名
     * @param in       输入流
     * @return 文件下载的URL地址
     */
    public static String getDownloadUrl(String fileName, InputStream in) {
        TempFileCache cache = CacheFactory.getCommonCacheFactory().getTempFileCache();
        return cache.saveAsUrl(fileName, in, 2 * 60);
    }

}
