package com.brilliance.isc.doc.service.impl;

import com.brilliance.isc.bo.Bnl;
import com.brilliance.isc.bo.Dga;
import com.brilliance.isc.bo.Smh;
import com.brilliance.isc.common.codetable.service.CodetableService;
import com.brilliance.isc.common.context.AppEnv;
import com.brilliance.isc.common.doc.TextResultVo;
import com.brilliance.isc.common.exception.CommonServiceException;
import com.brilliance.isc.common.tools.service.ObjectStroeToolService;
import com.brilliance.isc.common.trndoc.vo.DoceotVo;
import com.brilliance.isc.doc.constant.DocConstant;
import com.brilliance.isc.doc.convert.FormatHtmlHelper;
import com.brilliance.isc.doc.convert.FormatSwtHelper;
import com.brilliance.isc.doc.convert.FormatXmlHelper;
import com.brilliance.isc.doc.convert.ResultConvert;
import com.brilliance.isc.doc.handle.impl.common.SimpleTextProvider;
import com.brilliance.isc.doc.model.DocTreeNode;
import com.brilliance.isc.esb.model.ebmp.Req6701;
import com.brilliance.isc.esb.model.ebmp.Req6801;
import com.brilliance.isc.esb.model.ebmp.Req6901;
import com.brilliance.isc.esb.model.inner.InnerRes8680;
import com.brilliance.isc.esb.model.inner.InnerRes8758;
import com.brilliance.isc.mda.dao.BnlMapper;
import com.brilliance.isc.mda.dao.DgaMapper;
import com.brilliance.isc.mda.dao.SmhMapper;
import com.brilliance.isc.utils.DateUtils;
import com.brilliance.mda.runtime.mda.config.IniConfig;
import com.brilliance.util.StringUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itextpdf.text.pdf.BaseFont;
import freemarker.template.TemplateMethodModelEx;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import sun.misc.BASE64Decoder;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

/**
 * 展示报文通用服务
 */
@Slf4j
@Service
public class DocCommonService {
    private static final String FONT = "simhei.ttf";

    private String PDF_TEMPLATE_NAME = "JSN_PDF";
    private String EJS_PDF_TEMPLATE_NAME = "EJS_PDF";
    private String PURE_PDF_TEMPLATE_NAME = "PURE_PDF";
    @Autowired
    private IniConfig rcvIni;//收报通用码表

    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private AppEnv appEnv;
    @Autowired
    private ObjectStroeToolService objectStroeToolService;
    @Autowired
    private SmhMapper smhMapper;

    @Autowired(required = false)
    private CodetableService codetableService;

    @Autowired
    private DgaMapper dgaMapper;

    @Autowired
    private BnlMapper bnlMapper;


    /**
     * 根据当前这笔报文的smh信息对象判断对应的展示报文是否在对象存储上
     *
     * @param smh 当前这笔报文的smh信息对象
     */
    public boolean isExist(Smh smh) {
        if (ObjectUtils.isEmpty(smh)) {
            log.error("未查询到对应的smh信息");
            return false;
        }
        //查找对象存储上面是否有文件
        String docPth = smh.getDocpth();
        String docFil = smh.getDocfil();
        String docFxt = smh.getDocfxt();
        if (!StringUtils.hasLength(docPth) || !StringUtils.hasLength(docFil) || !StringUtils.hasLength(docFxt)) {
            return false;
        }
        String filPath = appEnv.getSubDataPath(docPth, docFil + "." + docFxt);
        String cleanPath = cleanPath(filPath);
        if (objectStroeToolService.isExistObjectFile(cleanPath)) {
            return true;
        }
        return false;
    }

    /**
     * 存储人行跨境支付展示报文
     *
     * @param supplier 懒加载调核心接口获取人行跨境支付报文
     * @param smhInr   smh主键
     */
    public boolean saveCipsDoc(Supplier<InnerRes8758> supplier, String smhInr) {
        try {
            Smh condition = new Smh();
            condition.setInr(smhInr);
            Smh smh = smhMapper.selectByPrimaryKey(condition);
            //判断文件是否存在，如果存在就不用再保存
            if (isExist(smh)) {
                if (log.isInfoEnabled()) {
                    log.info("smhinr={}对应的人行跨境支付报文文件已经存在不用重复保存", smhInr);
                }
                return true;
            }
            //不存在，调用核心CIPS报文查询8758接口获取返回值
            InnerRes8758 innerRes8758 = supplier.get();
            if (ObjectUtils.isEmpty(innerRes8758) || CollectionUtils.isEmpty(innerRes8758.list8758)) {
                log.warn("调核心接口查询人行跨境支付报文信息为空");
                return false;
            }
            //根据响应组DocTreeNode
            DocTreeNode root = DocTreeNode.createDocTreeNode();
            DocTreeNode head = root.getHead();
            //填充头公共处理，目前都是取得smh的字段
            fillHead(head, "Formatted incoming message / " + smh.getMsgtyp() + " Viewer");
            DocTreeNode body = root.getBody();
            InnerRes8758.Item8758 item8758 = innerRes8758.list8758.get(0);
            //具体展示报文组装
            if ("cips112".equals(smh.getMsgtyp())) {
                body.add("30A", item8758.qixiri);
                body.add("32A", item8758.huobdh + " " + convertMoney(item8758.zhifje));
                body.add("YJE", convertMoney(item8758.yszfje));
                body.add("HLV", convertRate(item8758.huilv1));
                body.add("CEE", addStbTxt(item8758.ywzldm, "KJYWZL"));
                body.add("DDD", item8758.dddbsh);
                body.add("BSH", item8758.bawbsh);
                body.add("FHM", item8758.fkrmch);
                body.add("50C", item8758.fukrzh);
                body.add("FHD1", item8758.fkrdz1);
                body.add("FKKH", item8758.fkrkhh);
                body.add("FZH", item8758.fkzjhh);
                body.add("FKJH", item8758.fkjjhh);
                body.add("SKH", item8758.skrmch);
                body.add("SHZ", item8758.shkrzh);
                body.add("SKKH", item8758.skrkhh);
                body.add("SZH", item8758.shzjhh);
                body.add("SKJH", item8758.shjjhh);
                body.add("FY1", item8758.fuynh1);
                body.add("FY2", item8758.fuynh2);
                body.add("FY3", item8758.fuynh3);
                body.add("FY4", item8758.fuynh4);
                body.add("ZJ1", item8758.zjg1hh);
                body.add("ZJ1M", item8758.zjg1mc);
                body.add("ZJ2", item8758.zjg2hh);
                body.add("ZJ2M", item8758.zjg2mc);
                body.add("FYS1", item8758.fuyns1);
                body.add("FYS2", item8758.fuyns2);
                body.add("FYS3", item8758.fuyns3);
                body.add("FYS4", item8758.fuyns4);
                body.add("CYJE", convertMoney(item8758.cyshje));
                body.add("CFKZ", addStbTxt(item8758.cfkzjl, "ZHJLEX"));
                body.add("CFKG", addStbTxt(item8758.cfkrgj, "CTYTXT"));
                body.add("CSKZ", addStbTxt(item8758.cskzjl, "ZHJLEX"));
                body.add("CSKG", addStbTxt(item8758.cskrgj, "CTYTXT"));
                body.add("RYBZ", selectTime(item8758.ryjibz));
                body.add("CZJ1", item8758.czjjg1);
                body.add("CZJ1M", item8758.czj1mc);
                body.add("CFY1", item8758.covfuy);
                body.add("CFY2", item8758.covfy2);
                body.add("CFY3", item8758.covfy3);
                body.add("CFY4", item8758.covfy4);
            } else if ("cips113".equals(smh.getMsgtyp())) {
                body.add("30A", item8758.qixiri);
                body.add("32A", item8758.huobdh + " " + convertMoney(item8758.zhifje));
                body.add("YJE", convertMoney(item8758.yszfje));
                body.add("HLV", convertRate(item8758.huilv1));
                body.add("CEE", addStbTxt(item8758.ywzldm, "KJYWZL"));
                body.add("DDD", item8758.dddbsh);
                body.add("BSH", item8758.bawbsh);
                body.add("FYCD", selectMsg(item8758.feiycd));
                body.add("50C", item8758.fukrzh);
                body.add("FKH", item8758.fkrmch);
                body.add("FHD1", item8758.fkrdz1);
                body.add("FHD2", item8758.fkrdz2);
                body.add("FHD3", item8758.fkrdz3);
                body.add("FHD4", item8758.fkrdz4);
                body.add("FHD5", item8758.fkrdz5);
                body.add("FHD6", item8758.fkrdz6);
                body.add("FHD7", item8758.fkrdz7);
                body.add("FKKH", item8758.fkrkhh);
                body.add("FZH", item8758.fkzjhh);
                body.add("FKJH", item8758.fkjjhh);
                body.add("SHZ", item8758.shkrzh);
                body.add("SHM", item8758.skrmch);
                body.add("SHD1", item8758.skrdz1);
                body.add("SHD2", item8758.skrdz2);
                body.add("SHD3", item8758.skrdz3);
                body.add("SHD4", item8758.skrdz4);
                body.add("SHD5", item8758.skrdz5);
                body.add("SHD6", item8758.skrdz6);
                body.add("SHD7", item8758.skrdz7);
                body.add("SKKH", item8758.skrkhh);
                body.add("SZH", item8758.shzjhh);
                body.add("SKJH", item8758.shjjhh);
                body.add("FEY1", convertMoney(item8758.feiyon));
                body.add("FYH1", item8758.fufeih);
                body.add("FY1BZ", item8758.huo1dh);
                body.add("FEY2", convertMoney(item8758.feiyn2));
                body.add("FYH2", item8758.ffeih2);
                body.add("FY2BZ", item8758.huo2dh);
                body.add("FKRZ", item8758.fkrzhj);
                body.add("FKRJ", addStbTxt(item8758.fkrzjl, "ZHJLEX"));
                body.add("FKRH", item8758.fkrzjh);
                body.add("FKRG", addStbTxt(item8758.fkrzgj, "CTYTXT"));
                body.add("SKRZ", item8758.skrzhj);
                body.add("SKRL", addStbTxt(item8758.skrzjl, "ZHJLEX"));
                body.add("SKRH", item8758.skrzjh);
                body.add("SKZG", addStbTxt(item8758.skrzgj, "CTYTXT"));
                body.add("RYB", selectTime(item8758.ryjibz));
                body.add("MXWB", item8758.mxywbs);
                body.add("MXJE", item8758.mxywje);
                body.add("FQYH", item8758.fqzyhh);
                body.add("JSYH", item8758.jszyhh);
                body.add("MXSH", item8758.mxywsh);
                body.add("MXZT", item8758.zhuant);
                body.add("HZZT", item8758.mxhzzt);
                body.add("PLZT", item8758.plmxzt);
                body.add("PLXX", item8758.plmxxx);
                body.add("SCBS", item8758.sccbis);
                body.add("FABS", item8758.faibis);
                body.add("SCMT", item8758.sccamt);
                body.add("PLZW", item8758.plzwzt);
                body.add("PLHZ", item8758.plhzzw);
                body.add("ZWZT", item8758.zywtzt);
                body.add("FY1", item8758.fuynh1);
                body.add("FY2", item8758.fuynh2);
                body.add("FY3", item8758.fuynh3);
                body.add("FY4", item8758.fuynh4);
                body.add("ZJ1", item8758.zjg1hh);
                body.add("ZJ1M", item8758.zjg1mc);
                body.add("ZJ2", item8758.zjg2hh);
                body.add("ZJ2M", item8758.zjg2mc);
                body.add("FYS1", item8758.fuyns1);
                body.add("FYS2", item8758.fuyns2);
                body.add("FYS3", item8758.fuyns3);
                body.add("FYS4", item8758.fuyns4);
            } else {
                body.add("30A", item8758.qixiri);
                body.add("32A", item8758.huobdh + " " + convertMoney(item8758.zhifje));
                body.add("YJE", convertMoney(item8758.yszfje));
                body.add("HLV", convertRate(item8758.huilv1));
                body.add("CEE", addStbTxt(item8758.ywzldm, "KJYWZL"));
                body.add("DDD", item8758.dddbsh);
                body.add("BSH", item8758.bawbsh);
                body.add("FYCD", selectMsg(item8758.feiycd));
                body.add("50C", item8758.fukrzh);
                body.add("FKH", item8758.fkrmch);
                body.add("FKKH", item8758.fkrkhh);
                body.add("FZH", item8758.fkzjhh);
                body.add("SHZ", item8758.shkrzh);
                body.add("SHM", item8758.skrmch);
                body.add("SKKH", item8758.skrkhh);
                body.add("SZH", item8758.shzjhh);
                body.add("SKJH", item8758.shjjhh);
                body.add("FEY1", convertMoney(item8758.feiyon));
                body.add("FYH1", item8758.fufeih);
                body.add("FY1BZ", item8758.huo1dh);
                body.add("FEY2", convertMoney(item8758.feiyn2));
                body.add("FYH2", item8758.ffeih2);
                body.add("FY2BZ", item8758.huo2dh);
                body.add("FKRJ", addStbTxt(item8758.fkrzjl, "ZHJLEX"));
                body.add("FKRH", item8758.fkrzjh);
                body.add("FKRG", addStbTxt(item8758.fkrzgj, "CTYTXT"));
                body.add("SKRZ", item8758.skrzhj);
                body.add("SKRL", addStbTxt(item8758.skrzjl, "ZHJLEX"));
                body.add("SKZG", addStbTxt(item8758.skrzgj, "CTYTXT"));
                body.add("RYB", selectTime(item8758.ryjibz));
                body.add("FY1", item8758.fuynh1);
                body.add("FY2", item8758.fuynh2);
                body.add("FY3", item8758.fuynh3);
                body.add("FY4", item8758.fuynh4);
                body.add("ZJ1", item8758.zjg1hh);
                body.add("ZJ1M", item8758.zjg1mc);
                body.add("ZJ2", item8758.zjg2hh);
                body.add("ZJ2M", item8758.zjg2mc);
                body.add("FYS1", item8758.fuyns1);
                body.add("FYS2", item8758.fuyns2);
                body.add("FYS3", item8758.fuyns3);
                body.add("FYS4", item8758.fuyns4);
            }
            //调通用存储展示报文逻辑
            return saveDoc(root, rcvIni, DocConstant.CIPS, smh);
        } catch (Exception e) {
            log.error("存储人行跨境支付展示报文异常", e);
            return false;
        }
    }


    /**
     * 存储大额展示报文
     *
     * @param supplier 报文
     * @param smhInr   smh主键
     */
    public boolean saveHvpsDoc(Supplier<InnerRes8680> supplier, String smhInr, String[] split) {
        try {
            Smh condition = new Smh();
            condition.setInr(smhInr);
            Smh smh = smhMapper.selectByPrimaryKey(condition);
            //判断文件是否存在，如果存在就不用再保存
            if (isExist(smh)) {
                if (log.isInfoEnabled()) {
                    log.info("smhinr={}对应的大额报文报文文件已经存在不用重复保存", smhInr);
                }
                return true;
            }
            //不存在，调用核心大额报文查询8680接口获取返回值
            InnerRes8680 innerRes8680 = supplier.get();
            if (ObjectUtils.isEmpty(innerRes8680)) {
                log.warn("调核心8680接口查询大额报文信息为空");
                return false;
            }
            //组展示报文
            DocTreeNode root = DocTreeNode.createDocTreeNode();
            DocTreeNode head = root.getHead();
            //填充头公共处理，目前都是取得smh的字段
            fillHead(head, "Formatted incoming message / " + smh.getMsgtyp() + " Viewer");
            DocTreeNode body = root.getBody();
            //具体展示报文组装
            body.add("30A", split[1]);
            body.add("32A", split[2] + " " + convertMoney(split[3]));
            body.add("52A", split[5]);
            body.add("fqNam_hide", GetBNLName(split[5]));
            body.add("CC4", split[6]);
            body.add("fkNam_hide", GetBNLName(split[6]));
            body.add("50C", split[7]);
            body.add("50A", split[8]);
            body.add("50B", split[9]);
            body.add("58A", split[11]);
            body.add("jsNam_hide", GetBNLName(split[11]));
            body.add("CC5", split[12]);
            body.add("skNam_hide", GetBNLName(split[12]));
            body.add("59C", split[13]);
            body.add("59A", split[14]);
            body.add("59B", split[15]);
            body.add("CEE", addStbTxt(split[16], "KJYWZL"));
            body.add("72A", split[20]);
            body.add("RMK", split[21]);
            if ("hvps141".equals(split[0])) {
                body.add("zqzchh_hide", innerRes8680.shdhmc);
                body.add("zqzrbs_hide", innerRes8680.jiybsh);
            }
            return saveDoc(root, rcvIni, DocConstant.HVPS, smh);
        } catch (Exception e) {
            log.error("存储大额展示报文异常", e);
            return false;
        }
    }

    /**
     * 存储二代支付展示报文
     *
     * @param msg    报文
     * @param smhInr smh主键
     */
    public boolean saveCnapDoc(String[] msg, String smhInr) {
        try {
            //判断报文格式(二代),目前取得RMBSettleFilter中的8458
            if (msg.length != 22) {
                log.error("二代支付报文信息格式不对");
                return false;
            }
            Smh condition = new Smh();
            condition.setInr(smhInr);
            Smh smh = smhMapper.selectByPrimaryKey(condition);
            //判断文件是否存在，如果存在就不用再保存
            if (isExist(smh)) {
                if (log.isInfoEnabled()) {
                    log.info("smhinr={}对应的二代支付报文文件已经存在不用重复保存", smhInr);
                }
                return true;
            }
            //组展示报文
            DocTreeNode root = DocTreeNode.createDocTreeNode();
            DocTreeNode head = root.getHead();
            //填充头公共处理，目前都是取得smh的字段
            fillHead(head, "Formatted incoming message / " + smh.getMsgtyp() + " Viewer");
            DocTreeNode body = root.getBody();
            //具体展示报文组装
            body.add("30A", msg[1]);
            body.add("32A", msg[2] + " " + convertMoney(msg[3]));
            body.add("52A", msg[5]);
            body.add("fqNam_hide", GetBNLName(msg[5]));
            body.add("CC4", msg[6]);
            body.add("fkNam_hide", GetBNLName(msg[6]));
            body.add("50C", msg[7]);
            body.add("50A", msg[8]);
            body.add("50B", msg[9]);
            body.add("58A", msg[11]);
            body.add("jsNam_hide", GetBNLName(msg[11]));
            body.add("CC5", msg[12]);
            body.add("skNam_hide", GetBNLName(msg[12]));
            body.add("59C", msg[13]);
            body.add("59A", msg[14]);
            body.add("59B", msg[15]);
            body.add("CEE", addStbTxt(msg[16], "KJYWZL"));
            body.add("72A", msg[20]);
            if ("beps125".equals(smh.getMsgtyp())) {
                body.add("RMK", msg[21]);
            }
            //调通用存储展示报文逻辑
            return saveDoc(root, rcvIni, DocConstant.CNAP, smh);
        } catch (Exception e) {
            log.error("存储二代支付展示报文异常", e);
            return false;
        }
    }

    /**
     * 存储境内外币展示报文
     *
     * @param msg    报文
     * @param smhInr smh主键
     */
    public boolean saveFxpsDoc(String[] msg, String smhInr) {
        try {
            //判断报文格式
            if (msg.length != 57) {
                log.error("境内外币报文信息格式不对");
                return false;
            }
            Smh condition = new Smh();
            condition.setInr(smhInr);
            Smh smh = smhMapper.selectByPrimaryKey(condition);
            //判断文件是否存在，如果存在就不用再保存
            if (isExist(smh)) {
                if (log.isInfoEnabled()) {
                    log.info("smhinr={}对应的境内外币报文报文文件已经存在不用重复保存", smhInr);
                }
                return true;
            }
            //组展示报文
            DocTreeNode root = DocTreeNode.createDocTreeNode();
            DocTreeNode head = root.getHead();
            //填充头公共处理，目前都是取得smh的字段
            fillHead(head, "境内外币 FXPS" + smh.getMsgtyp() + "报文");
            DocTreeNode body = root.getBody();
            //具体展示报文组装
            if ("111".equals(smh.getMsgtyp())) {
                body.add("FXPS", msg[5].substring(msg[5].length() - 3));
                body.add("20", msg[1]);
                body.add("baowbh_hide", msg[0]);
                body.add("23B", "CRED");
                body.add("32A", codetableService.getTblKey(msg[11], "CURKEY") + convertMoney(msg[12]));//币种代号还需要对应币种
                body.add("50K", "/" + msg[23]);
                body.add("50N", StringUtil.isEmpty(msg[24]) ? "" : "/A52/" + msg[24]);
                body.add("52A", msg[27]);
                body.add("52N", msg[28]);
                body.add("53A", msg[9]);
                body.add("FqNam_hide", getBicNam(msg[9]));
                body.add("54A", msg[13]);
                body.add("JsNam_hide", getBicNam(msg[13]));
                body.add("56A", msg[14]);
                body.add("JshMc_hide", getBicNam(msg[14]));
                body.add("57A", msg[36]);
                body.add("57N", msg[37]);
                body.add("59Z", "/" + msg[33]);
                body.add("59M", StringUtil.isEmpty(msg[34]) ? "" : "/A55/" + msg[34]);
                body.add("59D", StringUtil.isEmpty(msg[35]) ? "" : "/A56/" + msg[35]);
                body.add("72R", "/B00/" + msg[3]);
                body.add("72", StringUtil.isEmpty(msg[10]) ? "" : "/A30/" + msg[10]);
                body.add("FqhMc_hide", getBicNam(msg[10]));
                body.add("72L", StringUtil.isEmpty(msg[6]) ? "" : "/E25/" + addStbTxt(msg[6], "YWLXDM"));
                body.add("72Z", StringUtil.isEmpty(msg[38]) ? "" : "/E26/" + addStbTxt(msg[38], "HBSTYP"));
                body.add("72X", StringUtil.isEmpty(msg[26]) ? "" : "/G53/" + addStbTxt(msg[26], "ACTPRO"));
                body.add("72F", msg[47]);
                body.add("72Q", msg[7]);
            } else {
                body.add("FXPS", msg[5].substring(msg[5].length() - 3));
                body.add("20", msg[1]);
                body.add("baowbh_hide", msg[0]);
                body.add("21", msg[8]);
                body.add("32A", msg[3] + codetableService.getTblKey(msg[11], "CURKEY") + convertMoney(msg[12]));
                body.add("52A", StringUtil.isEmpty(msg[10]) ? "" : "/A30/" + msg[10]);
                body.add("FqhMc_hide", getBicNam(msg[10]));
                body.add("53A", msg[9]);
                body.add("FqNam_hide", getBicNam(msg[9]));
                body.add("54A", msg[13]);
                body.add("JsNam_hide", getBicNam(msg[13]));
                body.add("58A", msg[14]);
                body.add("JshMc_hide", getBicNam(msg[14]));
                body.add("72Z", StringUtil.isEmpty(msg[38]) ? "" : "/E26/" + addStbTxt(msg[38], "HBSTYP"));
                body.add("52a", msg[27]);
                body.add("57a", msg[36]);
                body.add("58a", msg[56]);
                body.add("72Q", msg[7]);
            }
            return saveDoc(root, rcvIni, DocConstant.FXPS, smh);
        } catch (Exception e) {
            log.error("存储存储境内外币展示报文异常", e);
            return false;
        }
    }

    /**
     * 存储swift渠道电商汇出展示报文
     *
     * @param req6701 报文
     * @param smhInr  smh主键
     */
    public boolean saveSwteDoc(Req6701 req6701, String smhInr) {
        try {
            //判断报文格式
            if (req6701 == null) {
                log.error("swift渠道电商汇出报文信息不存在");
                return false;
            }
            Smh condition = new Smh();
            condition.setInr(smhInr);
            Smh smh = smhMapper.selectByPrimaryKey(condition);
            //判断文件是否存在，如果存在就不用再保存
            if (isExist(smh)) {
                if (log.isInfoEnabled()) {
                    log.info("smhinr={}对应的swift渠道电商汇出报文文件已经存在不用重复保存", smhInr);
                }
                return true;
            }
            //组展示报文
            DocTreeNode root = DocTreeNode.createDocTreeNode();
            DocTreeNode head = root.getHead();
            //填充头公共处理，目前都是取得smh的字段
            //fillHead(head, "");
            DocTreeNode body = root.getBody();
            //具体展示报文组装
            body.add("ID", " ");
            body.add("OD", "");
            body.add("II", "EVERCNBJXXX BIC identified as:\nCHINA EVERBRIGHT BANK\n100033 BEIJING\nCHINA");
            body.add("MT", "103 Single Customer Credit Transfer");
            body.add("IO", "");
            body.add("108", "Reference");
            body.add("20", req6701.getSHLSH());
            body.add("23B", "CRED");
            body.add("32A", req6701.getJIYIBZ() + " " + convertMoney(req6701.getQISUJE()));
            body.add("50K", req6701.getHKRZHH() + "\n" + req6701.getBRANCH() + "\n" + req6701.getECIFNO() + "\n" + req6701.getYIWMCH() + "\n" + req6701.getYIWDZH());
            body.add("52A", "");
            body.add("56A", req6701.getSKRDLH());
            body.add("57A", req6701.getBICNUM());
            body.add("57D", "/" + req6701.getQISDAM() + "\n" + req6701.getSKYHMC() + "\n" + req6701.getSKYHDZ());
            body.add("59", req6701.getSKRZHH() + "\n" + req6701.getSKRMCH() + "\n" + req6701.getSKRDZH());
            body.add("70", req6701.getFUKXIX());
            body.add("71A", req6701.getFEYCHD());
            body.add("72", StringUtil.isEmpty(req6701.getFGSFYA()) ? "" : "/ACC/" + req6701.getFGSFYA());
            //调通用存储展示报文逻辑
            return saveDoc(root, rcvIni, DocConstant.SWTE, smh);
        } catch (Exception e) {
            log.error("存储swift渠道电商汇出展示报文异常", e);
            return false;
        }
    }


    /**
     * 存储CIPS渠道电商汇出展示报文
     *
     * @param req6801 报文
     * @param smhInr  smh主键
     */
    public boolean saveCipeDoc(Req6801 req6801, String smhInr) {
        try {
            //判断报文格式
            if (req6801 == null) {
                log.error("CIPS渠道电商汇出报文信息不存在");
                return false;
            }
            Smh condition = new Smh();
            condition.setInr(smhInr);
            Smh smh = smhMapper.selectByPrimaryKey(condition);
            //判断文件是否存在，如果存在就不用再保存
            if (isExist(smh)) {
                if (log.isInfoEnabled()) {
                    log.info("smhinr={}对应的CIPS渠道电商汇出报文文件已经存在不用重复保存", smhInr);
                }
                return true;
            }
            //组展示报文
            DocTreeNode root = DocTreeNode.createDocTreeNode();
            DocTreeNode head = root.getHead();
            //填充头公共处理，目前都是取得smh的字段
            //fillHead(head, "");
            DocTreeNode body = root.getBody();
            //具体展示报文组装
            List<Req6801.ItemF68011> f68011 = req6801.F68011;
            if (CollectionUtils.isEmpty(f68011)) {
                log.warn("6801接收对应CIPE报文内容为空");
            }
            body.add("ID", " ");
            body.add("OD", "");
            body.add("II", "EVERCNBJXXX BIC identified as:\nCHINA EVERBRIGHT BANK\n100033 BEIJING\nCHINA");
            body.add("MT", req6801.getBWLXDH());
            body.add("IO", "");
            body.add("108", "Reference");
            body.add("20", req6801.getSHLSH());
            body.add("23B", "CRED");
            body.add("32A", "CNY " + convertMoney(req6801.getZHIFJE()));
            body.add("50K", f68011.get(0).getFUKRZH() + "\n" + req6801.getBRANCH() + "\n" + req6801.getECIFNO() + "\n" + "" + "\n" + "");
            body.add("52A", "");
            body.add("56A", "");
            body.add("59", f68011.get(0).getSHKRZH() + "\n" + f68011.get(0).getSKRMCH() + "\n" + "");
            body.add("70", "");
            body.add("71A", "");
            body.add("72", "");
            //调通用存储展示报文逻辑
            return saveDoc(root, rcvIni, DocConstant.CIPE, smh);
        } catch (Exception e) {
            log.error("存储CIPS渠道电商汇出展示报文异常", e);
            return false;
        }
    }


    /**
     * 存储FXPS渠道电商汇出展示报文
     *
     * @param req6901 报文
     * @param smhInr  smh主键
     */
    public boolean saveFxpeDoc(Req6901 req6901, String smhInr) {
        try {
            //判断报文格式
            if (req6901 == null) {
                log.error("FXPS渠道电商汇出报文信息不存在");
                return false;
            }
            Smh condition = new Smh();
            condition.setInr(smhInr);
            Smh smh = smhMapper.selectByPrimaryKey(condition);
            //判断文件是否存在，如果存在就不用再保存
            if (isExist(smh)) {
                if (log.isInfoEnabled()) {
                    log.info("smhinr={}对应的FXPS渠道电商汇出报文文件已经存在不用重复保存", smhInr);
                }
                return true;
            }
            //组展示报文
            DocTreeNode root = DocTreeNode.createDocTreeNode();
            DocTreeNode head = root.getHead();
            //填充头公共处理，目前都是取得smh的字段
            //fillHead(head, "");
            DocTreeNode body = root.getBody();
            //具体展示报文组装
            body.add("ID", " ");
            body.add("OD", "");
            body.add("II", "EVERCNBJXXX BIC identified as:\nCHINA EVERBRIGHT BANK\n100033 BEIJING\nCHINA");
            body.add("MT", "FXPS111");
            body.add("IO", "");
            body.add("108", "Reference");
            body.add("20", req6901.getSHLSH());
            body.add("23B", "CRED");
            body.add("32A", req6901.getJIYIBZ() + " " + convertMoney(req6901.getQISUJE()));
            body.add("50K", req6901.getHKRZHH() + "\n" + req6901.getBRANCH() + "\n" + req6901.getECIFNO() + "\n" + req6901.getYIWMCH() + "\n" + req6901.getYIWDZH());
            body.add("52A", "");
            body.add("56A", "");
            body.add("57A", req6901.getBICNUM());
            body.add("59", req6901.getSKRZHH() + "\n" + req6901.getSKRMCH() + "\n" + req6901.getSKRDZH());
            body.add("70", "");
            body.add("71A", "");
            body.add("72", StringUtil.isEmpty(req6901.getFGSFYA()) ? "" : "/ACC/" + req6901.getFGSFYA());
            //调通用存储展示报文逻辑
            return saveDoc(root, rcvIni, DocConstant.FXPE, smh);
        } catch (Exception e) {
            log.error("存储FXPS渠道电商汇出展示报文异常", e);
            return false;
        }
    }


    /**
     * 按照doc通用展示数据格式存储来报（大额、境内外币、跨境和二代支付）{@link ResultConvert.TagDesp}
     *
     * @param root    展示报文树根
     * @param config  展示报文中文码表
     * @param categry 报文种类
     * @param smh     smh数据
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public boolean saveDoc(DocTreeNode root, IniConfig config, String categry, Smh smh) throws Exception {
        String smhInr = smh.getInr();
        if (log.isInfoEnabled()) {
            log.info("开始保存展示报文:{}", smhInr);
        }
        Map codeTable = config.getBlock(categry);
        codeTable.putAll(config.getBlock("HEAD"));
        List<ResultConvert.TagDesp> showList = new ArrayList<>();
        BiConsumer<DocTreeNode, Integer> consumer = (node, level) -> {
            String tag = node.getTag();
            if (tag.matches("ROOT|HEAD|BODY")) {
                return;
            }
            ResultConvert.TagDesp tagDesp = new ResultConvert.TagDesp();
            if (!node.isNode()) {
                String value = node.getData();
                if (StringUtils.hasText(value)) {
                    value = value.trim();
                }
                tagDesp.setValue(value);
            }
            String desp = convertKey(codeTable, tag, smh.getMsgtyp());
            tagDesp.setDesp(desp);
            tagDesp.setLvl(level);
            if (StringUtil.isNotEmpty(tag) && tag.endsWith("_hide")) {
                tagDesp.setTag("");
            } else {
                tagDesp.setTag(tag);
            }
            showList.add(tagDesp);
        };
        //ROOT,HEAD和BODY这两级跳过，因此level设置为-2
        root.preOrderTraversal(consumer, -2);
        //数据存储到对象存储
        byte[] content = objectMapper.writeValueAsBytes(showList);
        String docPth = "DOCS/" + DateUtils.dateTimeNow("yyyyMM");
        String docFil = smhInr;
        String docFxt = categry;
        String filPath = appEnv.getSubDataPath(docPth, docFil + "." + docFxt);
        String cleanPath = cleanPath(filPath);
        boolean flag = objectStroeToolService.saveObjectFileByStream(new ByteArrayInputStream(content), cleanPath);
        if (!flag) {
            log.error("保存展示报文失败:{}", smhInr);
            return false;
        }
        //保存pdf到对象存储
        if (log.isInfoEnabled()) {
            log.info("开始保存PDF文件:{}", smhInr);
        }
        byte[] pdfContent = renderJsnMessageObjectToPdf(showList, getMsgTyp(smh));
        filPath = appEnv.getSubDataPath(docPth, docFil + ".pdf");
        cleanPath = cleanPath(filPath);
        flag = objectStroeToolService.saveObjectFileByStream(new ByteArrayInputStream(pdfContent), cleanPath);
        if (!flag) {
            log.error("保存PDF文件失败:{}", smhInr);
            return false;
        }
        //更新smh表相关信息
        if (log.isInfoEnabled()) {
            log.info("开始更新SMH信息:{}", smhInr);
        }
        smh.setDocfil(docFil);
        smh.setDocfxt(docFxt);
        smh.setDocpth(docPth);
        int updateSuccess = smhMapper.updateByPrimaryKeySelective(smh);
        if (updateSuccess <= 0) {
            log.warn("更新SMH信息失败或smh报文文件信息已存在:{}", smhInr);
        }
        return true;
    }

    private void fillHead(DocTreeNode head, String title) {
        head.add("__TITLE", title);
    }

    private String getMsgTyp(Smh smh) {
        return smh.getExtkey();
    }

    private String convertKey(Map<String, String> codeTable, String tag, String msgTyp) {
        if (ObjectUtils.isEmpty(codeTable)) {
            return tag;
        }
        //20240618根据类型判断，映射标签拼接
        String convertTag = codeTable.get(tag + "_" + msgTyp);
        if (StringUtils.hasLength(convertTag)) {
            return convertTag;
        } else {
            convertTag = codeTable.get(tag);
            return StringUtils.hasLength(convertTag) ? convertTag : tag;
        }

    }

    private String cleanPath(String path) {
        if (!StringUtils.hasLength(path)) {
            return "";
        }
        if (path.startsWith("/") && path.length() > 1) {
            path = path.substring(1);
        }
        path = path.replace("\\", "/");
        return path;
    }

    /**
     * 生成人行四类来报的PDF
     */
    public byte[] renderJsnMessageObjectToPdf(List<ResultConvert.TagDesp> showMessage, String msgtyp) {
        SimpleTextProvider simpleTextProvider = SimpleTextProvider.builder().onlyHtml(true)
                .docName(PDF_TEMPLATE_NAME).templateName(PDF_TEMPLATE_NAME + "_DOC.ftl").build();
        Map<String, Object> data = new HashMap<>();
        DoceotVo doceotVo = new DoceotVo();
        doceotVo.setDocuil("CN");
        data.put("tagDespList", showMessage);
        data.put("msgtyp", msgtyp);
        data.put("date", new SimpleDateFormat("dd.MM.yyyy").format(new Date()));
        data.put("time", new SimpleDateFormat("HH:mm:ss").format(new Date()));
        data.put("formatContent", new FormatHtmlHelper());
        TextResultVo textResultVo = simpleTextProvider.outputMessage(data, doceotVo, data);

        //html转pdf
        //将html 内容bos转为PDF
        try (ByteArrayOutputStream bos2OfPdf = new ByteArrayOutputStream()) {
            String html = textResultVo.getData();
            byte[] buff = new BASE64Decoder().decodeBuffer(html);
            html = new String(buff, "utf-8");
            ITextRenderer render = new ITextRenderer();
            ITextFontResolver fontResolver = render.getFontResolver();
            fontResolver.addFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            render.setDocumentFromString(html);
            render.layout();
            render.createPDF(bos2OfPdf);
            return bos2OfPdf.toByteArray();
        } catch (Exception e) {
            throw new CommonServiceException("pdf生成异常");
        }
    }

    /**
     * 生成类似E结算面函模式的PDF
     */
    public byte[] renderEjsMessageObjectToPdf(List<ResultConvert.TagDesp> showMessage) {
        SimpleTextProvider simpleTextProvider = SimpleTextProvider.builder().onlyHtml(true)
                .docName(EJS_PDF_TEMPLATE_NAME).templateName(EJS_PDF_TEMPLATE_NAME + "_DOC.ftl").build();
        Map<String, Object> data = new HashMap<>();
        DoceotVo doceotVo = new DoceotVo();
        doceotVo.setDocuil("CN");
        data.put("tagDespList", showMessage);
        data.put("date", new SimpleDateFormat("dd.MM.yyyy").format(new Date()));
        data.put("time", new SimpleDateFormat("HH:mm:ss").format(new Date()));
        data.put("formatContent", new FormatSwtHelper());
        TextResultVo textResultVo = simpleTextProvider.outputMessage(data, doceotVo, data);

        //html转pdf
        //将html 内容bos转为PDF
        try (ByteArrayOutputStream bos2OfPdf = new ByteArrayOutputStream()) {
            String html = textResultVo.getData();
            byte[] buff = new BASE64Decoder().decodeBuffer(html);
            html = new String(buff, "utf-8");
            ITextRenderer render = new ITextRenderer();
            ITextFontResolver fontResolver = render.getFontResolver();
            fontResolver.addFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            render.setDocumentFromString(html);
            render.layout();
            render.createPDF(bos2OfPdf);
            return bos2OfPdf.toByteArray();
        } catch (Exception e) {
            throw new CommonServiceException("pdf生成异常");
        }
    }

    /**
     * 面函转PDF通用方法
     *
     * @param showMessage 面函数据结构
     * @param type        面函种类（ELC,EJS,JSN）
     */
    public byte[] renderMessageObjectToPdf(List<ResultConvert.TagDesp> showMessage, String type,TemplateMethodModelEx methodModelEx) {
        String TEMPLATE_NAME = type.toUpperCase();
        SimpleTextProvider simpleTextProvider = SimpleTextProvider.builder().onlyHtml(true)
                .docName(TEMPLATE_NAME).templateName(TEMPLATE_NAME + "_PDF_DOC.ftl").build();
        Map<String, Object> data = new HashMap<>();
        DoceotVo doceotVo = new DoceotVo();
        doceotVo.setDocuil("CN");
        data.put("tagDespList", showMessage);
        data.put("date", new SimpleDateFormat("dd.MM.yyyy").format(new Date()));
        data.put("time", new SimpleDateFormat("HH:mm:ss").format(new Date()));
        data.put("formatContent", methodModelEx);
        TextResultVo textResultVo = simpleTextProvider.outputMessage(data, doceotVo, data);

        //html转pdf
        //将html 内容bos转为PDF
        try (ByteArrayOutputStream bos2OfPdf = new ByteArrayOutputStream()) {
            String html = textResultVo.getData();
            byte[] buff = new BASE64Decoder().decodeBuffer(html);
            html = new String(buff, "utf-8");
            ITextRenderer render = new ITextRenderer();
            ITextFontResolver fontResolver = render.getFontResolver();
            fontResolver.addFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            render.setDocumentFromString(html);
            render.layout();
            render.createPDF(bos2OfPdf);
            return bos2OfPdf.toByteArray();
        } catch (Exception e) {
            throw new CommonServiceException("生成" + type + "种类PDF异常");
        }
    }

    public byte[] renderPureMessageObjectToPdf(String msg, String msgType) {
        SimpleTextProvider simpleTextProvider = SimpleTextProvider.builder().onlyHtml(true).
                docName(PURE_PDF_TEMPLATE_NAME).templateName(PURE_PDF_TEMPLATE_NAME + "_LET.ftl").build();
        Map<String, Object> data = new HashMap<>();
        DoceotVo doceotVo = new DoceotVo();
        doceotVo.setDocuil("CN");
        data.put("msg", msg);
        data.put("date", new SimpleDateFormat("dd.MM.yyyy").format(new Date()));
        data.put("time", new SimpleDateFormat("HH:mm:ss").format(new Date()));
        TemplateMethodModelEx methodModelEx;
        if ("xml".equalsIgnoreCase(msgType)) {
            methodModelEx = new FormatXmlHelper();
        } else {
            methodModelEx = new FormatSwtHelper();
        }
        data.put("formatContent", methodModelEx);
        TextResultVo textResultVo = simpleTextProvider.outputMessage(data, doceotVo, data);

        //html转pdf
        //将html 内容bos转为PDF
        try (ByteArrayOutputStream bos2OfPdf = new ByteArrayOutputStream()) {
            String html = textResultVo.getData();
            byte[] buff = new BASE64Decoder().decodeBuffer(html);
            html = new String(buff, "utf-8");
            ITextRenderer render = new ITextRenderer();
            ITextFontResolver fontResolver = render.getFontResolver();
            fontResolver.addFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

            render.setDocumentFromString(html);
            render.layout();
            render.createPDF(bos2OfPdf);
            textResultVo.setData(null);
            textResultVo.setFormat("pdf");
            textResultVo.setExtension("pdf");
            return bos2OfPdf.toByteArray();
        } catch (Exception e) {
            throw new CommonServiceException("Pdf生成异常");
        }
    }

    /**
     * 给金额数据赋默认值
     *
     * @param msg
     * @return
     */
    private String convertMoney(String msg) {
        return convertNum(msg, "0,00");
    }

    /**
     * 给利率数据赋默认值
     *
     * @param msg
     * @return
     */
    private String convertRate(String msg) {
        return convertNum(msg, "0,00000000");
    }

    private String convertNum(String raw, String def) {
        return StringUtils.hasLength(raw) ? raw : def;
    }


    /**
     * 处理码表拼接信息
     *
     * @param cod
     * @param tbl
     * @return
     */
    private String addStbTxt(String cod, String tbl) {
        String tblKey = codetableService.getTblLabel(cod, tbl);
        return cod + "-" + tblKey;
    }


    /**
     * 费用承担方判断
     *
     * @param feiycd
     * @return
     */
    private String selectMsg(String feiycd) {
        switch (feiycd) {
            case "CRED":
                return "接收方付费";
            case "DEBT":
                return "发起方付费";
            case "SHAR":
                return "共同承担";
            case "SLEV":
                return "双方约定的方式";
            default:
                return "";
        }
    }


    /**
     * 日夜间状态判断
     *
     * @param ryjibz
     * @return
     */
    private String selectTime(String ryjibz) {
        switch (ryjibz) {
            case "DAYT":
                return ryjibz + "-" + "日间";
            case "NITT":
                return ryjibz + "-" + "夜间";
            default:
                return ryjibz;
        }
    }

    /**
     * 境内外币通过bic行号获取DGA表行名
     *
     * @param biccode
     * @return
     */
    private String getBicNam(String biccode) {
        List<Dga> dgas = dgaMapper.selectByBiccod(biccode);
        if (CollectionUtils.isEmpty(dgas)) {
            return "";
        }
        return dgas.get(0).getJigomc();
    }

    /**
     * 二代支付根据行号查询BNL表中的行名
     *
     * @param fqcyjg
     * @return
     */
    private String GetBNLName(String fqcyjg) {
        Bnl bnl = bnlMapper.selectByFqcyjg(fqcyjg);
        if (bnl == null) {
            return "";
        }
        return bnl.getFukrhm();
    }
}
