package nckd.jimin.jyyy.fi.webapi;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import kd.bos.cache.CacheFactory;
import kd.bos.cache.TempFileCache;
import kd.bos.dataentity.OperateOption;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.dataentity.entity.DynamicObjectCollection;
import kd.bos.dataentity.resource.ResManager;
import kd.bos.dataentity.utils.StringUtils;
import kd.bos.entity.BillEntityType;
import kd.bos.entity.operate.result.OperationResult;
import kd.bos.logging.Log;
import kd.bos.logging.LogFactory;
import kd.bos.openapi.common.custom.annotation.ApiController;
import kd.bos.openapi.common.custom.annotation.ApiParam;
import kd.bos.openapi.common.custom.annotation.ApiPostMapping;
import kd.bos.openapi.common.result.CustomApiResult;
import kd.bos.orm.query.QCP;
import kd.bos.orm.query.QFilter;
import kd.bos.servicehelper.AttachmentServiceHelper;
import kd.bos.servicehelper.BusinessDataServiceHelper;
import kd.bos.servicehelper.DispatchServiceHelper;
import kd.bos.servicehelper.operation.SaveServiceHelper;
import kd.imc.rim.common.message.exception.MsgException;
import kd.imc.rim.common.utils.FileUploadUtils;
import kd.imc.rim.common.utils.FileUtils;
import kd.imc.rim.common.utils.MD5;
import nckd.base.helper.CommonHelperUtils;
import nckd.jimin.jyyy.fi.common.XmlUtils;
import nckd.jimin.jyyy.fi.plugin.operate.SRMHelperUtils;

import javax.validation.Valid;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

@ApiController(
        desc = "对接SRM预付款、付款附件，上传到发票",
        value = "SRM"
)
public class SRMSynAttacmentApiPlugin implements Serializable {
    private static final long serialVersionUID = 8975925104443178270L;

    private static final Log logger = LogFactory.getLog(SRMSynAttacmentApiPlugin.class);

    @ApiPostMapping("synAttacmenthForSRM")
    public CustomApiResult<JSONObject> synAttacmenthForSRM(
            @Valid @ApiParam(value = "附件xml") String request) {

        //获取srm token
        Map<String,String> tokenMap = SRMHelperUtils.getSRMToken();
        if (!"0".equals(tokenMap.get("code"))) {//未获取到token
            logger.info("get srm token fail" + tokenMap.get("msg"));

            return returnResult("1000", "失败，获取token失败，" + tokenMap.get("msg"), null);
        }
        String token = tokenMap.get("msg").toString();

        if(StringUtils.isEmpty(request)){
            return returnResult("1000", "入参request不能为空，", null);
        }
        request = parseXMLString(request);
        request = request.replace("\r\n", "");

        try {
            JSONObject requestJSON = XmlUtils.documentToJSONObject(request);
            JSONObject billBody = requestJSON.getJSONArray("BillBody").getJSONObject(0);
            JSONObject bill = billBody.getJSONArray("Bill").getJSONObject(0);

            JSONArray files = billBody.getJSONArray("Document").getJSONObject(0).getJSONArray("Files");
            if(files == null || files.size() == 0){
                return returnResult("1000", "无file参数", null);
            }

            JSONArray fileArray = files.getJSONObject(0).getJSONArray("File");

            String billNO = bill.getString("billcode");
            String billType = bill.getString("billtype");
            String entityName = "";

            if("RC".equals(billType) || "XM".equals(billType)){
                entityName = "er_publicreimbursebill";
            } else if("YF".equals(billType)){
                entityName = "er_prepaybill";
            }

            if(StringUtils.isEmpty(billNO)){
                return returnResult("1000", "单据号(billcode)参数为空", null);
            }

            if(StringUtils.isEmpty(entityName)){
                return returnResult("1000", "单据类型(" + billType + ")不匹配，只支持RC、XM、YF", null);
            }

            QFilter qFilter = new QFilter("billno", QCP.equals, billNO);

            DynamicObject billInfo = BusinessDataServiceHelper.loadSingle(entityName, qFilter.toArray());

            if(billInfo == null){
                return returnResult("1000", "失败，单据号（" + billNO + "）在星瀚中未匹配到单据", null);
            }

            Map<String, Object> attachmentInfos = new HashMap<>();
            List<Map<String, Object>> attachments = new ArrayList<>();

            for(int i = 0; i < fileArray.size(); i++){
                JSONObject fileItem = fileArray.getJSONObject(i);
                String fileSize = fileItem.getString("FILE_SIZE");
                String fileName = fileItem.getString("FILE_NAME");
                String fileType = fileItem.getString("FILE_TYPE");
                String fileUrl = fileItem.getString("URL");

                fileUrl = fileUrl.replace("&amp;", "&");
                String downloadUrl = fileUrl + "&access_token=" + token;

                logger.info("SRMSynAttacmentApiPlugin：开始处理文件，downloadUrl " + downloadUrl);
                logger.info("SRMSynAttacmentApiPlugin：文件名 " + fileName);

                URL url = new URL(downloadUrl);
                //这里没有使用 封装后的ResponseEntity 就是也是因为这里不适合一次性的拿到结果，放不下content,会造成内存溢出
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();

                int size = connection.getContentLength();

                //使用bufferedInputStream 缓存流的方式来获取下载文件，不然大文件会出现内存溢出的情况
                InputStream inputStream = new BufferedInputStream(connection.getInputStream());

//                logger.info("SRMSynAttacmentApiPlugin：开始读取文件流 " + CommonHelperUtils.getDateStringFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));

//                byte[] fileData = InputStreamToBytes(inputStream);

//                logger.info("SRMSynAttacmentApiPlugin：读取文件流-结束 " + CommonHelperUtils.getDateStringFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));

                try {
                    TempFileCache cache = CacheFactory.getCommonCacheFactory().getTempFileCache();

                    logger.info("SRMSynAttacmentApiPlugin：缓存文件-开始 " + CommonHelperUtils.getDateStringFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));

                    String tempUrl = cache.saveAsFullUrl(fileName, inputStream, 3600);

                    logger.info("SRMSynAttacmentApiPlugin：缓存文件-结束 " + CommonHelperUtils.getDateStringFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));

                    StringBuffer uid = new StringBuffer();
                    uid.append("rc-upload-").append(new Date().getTime()).append("-");
                    Map<String, Object> attachmentInfo = new HashMap<>();
                    attachmentInfo.put("entityNumber", entityName);
                    attachmentInfo.put("name", fileName);
                    attachmentInfo.put("url", tempUrl);
                    attachmentInfo.put("size", fileSize);
//                attachmentInfo.put("status", map.get("status"));
                    attachmentInfo.put("uid", uid.toString() + i);

                    attachments.add(attachmentInfo);
                } catch (Exception e){
                    logger.info("synAttacmenthForSRM：缓存文件失败，" + (e.getMessage() == null ? "" : e.getMessage()));
                } finally {
                    try {
                        if(inputStream!=null){
                            inputStream.close();
                        }
                        if(connection !=null){
                            connection.disconnect();
                        }
                    } catch (Exception e) {
                        logger.info("附件上传失败,堆栈信息01:",e.getMessage());
                    }
                }
            }

            attachmentInfos.put("attachmentpanel",attachments);

            BillEntityType dataEntityType = (BillEntityType) billInfo.getDataEntityType();
            String appId = dataEntityType.getAppId();

            DynamicObjectCollection attcachCols = AttachmentServiceHelper.saveTempAttachments(entityName, billInfo.getLong("id"), appId, attachmentInfos);

            OperationResult resultSave = SaveServiceHelper.saveOperate(entityName, new DynamicObject[]{billInfo}, OperateOption.create());

            StringBuilder err = new StringBuilder();
            if (resultSave.getSuccessPkIds().size() <= 0) {
                for (int i = 0; i < resultSave.getAllErrorOrValidateInfo().size(); i++) {
                    String message = resultSave.getAllErrorOrValidateInfo().get(i).getMessage();
                    err.append("/").append(message);
                }
                logger.info(err.toString());

                return returnResult("1000", "附件上传后，保存单据失败：" + err.toString(), null);
            }
        } catch (IOException e) {
//            e.printStackTrace();
            logger.info("synAttacmenthForSRM：IOException " + (e.getMessage() == null ? "" : e.getMessage()));

            return returnResult("1000", "附件保存异常" + e.getMessage(), null);
        } catch(Exception e){
            logger.info("synAttacmenthForSRM：Exception " + (e.getMessage() == null ? "" : e.getMessage()));

            return returnResult("1000", "附件保存异常" + e.getMessage(), null);
        }

        return returnResult("0000", "附件同步成功", null);
    }

    private String upFileOfBase64(String base64, String fileType, String fileName) {
        String url = FileUploadUtils.getInvoiceDir("invoice") + fileName;
        String fileId = MD5.SHA256Hex(fileType + base64);
        String path = FileUtils.queryFilePath(fileId);
        if (StringUtils.isEmpty(path)) {
            path = FileUploadUtils.uploadBase64(url, fileName, base64);
            FileUtils.saveFilePath("", path, fileId, fileType);
        }

        logger.info("SRMSynAttacmentApiPlugin 上传服务器：fileName：" + fileName + ", url：" + url + ", fileId: " + fileId);
        logger.info("SRMSynAttacmentApiPlugin：" + (path == null ? "path is null" : path));

        return path;
    }

    private String upFileOfBytes(byte[] bytes, String fileType, String fileName) {
        String url = FileUploadUtils.getInvoiceDir("invoice") + fileName;
        String fileId = MD5.SHA256Hex(fileType + bytes.toString());
        String path = FileUtils.queryFilePath(fileId);
        if (StringUtils.isEmpty(path)) {
            path = FileUploadUtils.uploadBytes(url, fileName, bytes);
            FileUtils.saveFilePath("", path, fileId, fileType);
        }

        logger.info("SRMSynAttacmentApiPlugin 上传服务器：fileName：" + fileName + ", url：" + url + ", fileId: " + fileId);
        logger.info("SRMSynAttacmentApiPlugin：" + (path == null ? "path is null" : path));

        return path;
    }



    private String fileName(String fileType) {
        String fileName = String.valueOf(UUID.randomUUID());
        String filePix = "";
        if (StringUtils.equals(fileType, "1")) {
            filePix = ".pdf";
        } else if (StringUtils.equals(fileType, "2")) {
            filePix = ".png";
        } else if (StringUtils.equals(fileType, "4")) {
            filePix = ".ofd";
        } else if (StringUtils.equalsIgnoreCase(fileType, "pdf")) {
            filePix = ".pdf";
        } else if (StringUtils.equalsIgnoreCase(fileType, "ofd")) {
            filePix = ".ofd";
        } else if (FileUtils.isImage(fileType)) {
            filePix = ".jpg";
        } else {
            filePix = "." + fileType;
        }

        if (StringUtils.isEmpty(filePix)) {
            throw new MsgException("0009", ResManager.loadKDString("文件类型错误", "UploadFileService_1", "imc-rim-formplugin", new Object[0]));
        } else {
            return fileName + filePix;
        }
    }

    /**
     * InputStream转成base64String
     * @param inputStream
     * @return
     * @throws IOException
     */
    private String InputStreamToBase64String(InputStream inputStream){
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024]; // 创建一个缓冲区，大小可以根据需要调整
//        byte[] buffer = new byte[1024 * 1024 * 5];// 5MB
        int bytesRead;
        try {
            // 读取数据到缓冲区，直到没有更多数据可读
            while (true) {
                try {
                    if (!((bytesRead = inputStream.read(buffer)) != -1)) break;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                byteArrayOutputStream.write(buffer, 0, bytesRead);
            }

            // 将ByteArrayOutputStream的内容转换成byte数组
            byte[] bytes = byteArrayOutputStream.toByteArray();

//            String base64encode = new BASE64Encoder().encode(bytes);
            String base64String = Base64.getEncoder().encodeToString(bytes);
//去掉空格，直接转的话会有换行问题，在前端展示会导致出不来          base64encode = base64encode.replaceAll("[\\s*\t\n\r]", "");
            byteArrayOutputStream.close();

            return base64String;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }

    private byte[] InputStreamToBytes(InputStream inputStream){
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//        byte[] buffer = new byte[1024]; // 创建一个缓冲区，大小可以根据需要调整
        byte[] buffer = new byte[1024 * 1024 * 5];// 5MB
        int bytesRead;
        try {
            // 读取数据到缓冲区，直到没有更多数据可读
            while (true) {
                try {
                    if (!((bytesRead = inputStream.read(buffer)) != -1)) break;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                byteArrayOutputStream.write(buffer, 0, bytesRead);
            }

            // 将ByteArrayOutputStream的内容转换成byte数组
            byte[] bytes = byteArrayOutputStream.toByteArray();

            byteArrayOutputStream.close();

            return bytes;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }


    public static String parseXMLString(String xml) {
        int strIndex = xml.indexOf("<?xml ");
        int endIndex = xml.indexOf("</DATA>");
        String conStr = xml.substring(strIndex, endIndex + 7);
//        String dataStr = staStr + conStr;

        return conStr;
//        parseXML(dataStr);
    }

    /**
     * 自定义返回data对象
     * @param code  编码
     * @param message  错误信息
     * @return   CustomApiResult  返回
     */
    public CustomApiResult<JSONObject> returnResult(String code, String message, JSONObject responseData){
        JSONObject reslutData = new JSONObject();

        //0000-表示成功其他-表示失败
        reslutData.put("RSPCODE", code);
        reslutData.put("RSPMSG", message);
//        reslutData.put("data", responseData);

        return CustomApiResult.success(reslutData);
    }
}
