package com.kingdee.eas.custom.esign.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.kingdee.eas.base.permission.UserInfo;
import org.apache.commons.lang3.StringUtils;
import com.kingdee.bos.BOSException;
import com.kingdee.bos.Context;
import com.kingdee.bos.dao.ormapping.ObjectUuidPK;
import com.kingdee.bos.metadata.entity.EntityViewInfo;
import com.kingdee.bos.metadata.entity.FilterInfo;
import com.kingdee.bos.metadata.entity.FilterItemInfo;
import com.kingdee.bos.metadata.entity.SelectorItemCollection;
import com.kingdee.bos.metadata.query.util.CompareType;
import com.kingdee.eas.base.attachment.*;
import com.kingdee.eas.basedata.person.PersonInfo;
import com.kingdee.eas.common.EASBizException;
import com.kingdee.eas.custom.esign.*;
import com.kingdee.eas.custom.esign.tsign.hz.comm.EsignHttpResponse;
import com.kingdee.shr.attachment.*;
import com.kingdee.shr.attachment.AttachmentTypeEnum;
import com.kingdee.shr.base.syssetting.exception.ShrWebBizException;
import com.kingdee.shr.base.syssetting.ml.SHRWebResource;
import com.kingdee.shr.ml.util.MutilanUtils;
import com.kingdee.util.LocaleUtils;
import com.kingdee.util.STConverter;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

/**
 * @Description 签署文件同步
 * @Date 2025/11/29 14:22
 * @Created by heyuan
 */
public class SyncSignedFilesUtil {
    private static Logger logger = Logger.getLogger(SyncSignedFilesUtil.class);

    // 员工页签的uipk
    private static final String EMPPAGEUIPK = "com.kingdee.eas.custom.esign.app.FileTab.form";

    private static final SelectorItemCollection updateSic = new SelectorItemCollection();

    static {
        updateSic.add("Fivouchered");
    }

    /**
     * 同步员工页面的电子签附件信息。
     * <p>
     * 根据传入的签署ID（signId），从电子签全域状态总览表中获取对应的记录，
     * 并判断是否已同步过附件。如果尚未同步，则通过e签宝接口下载相关文件和附件，
     * 并保存到系统中。
     * </p>
     *
     * @param ctx    上下文对象，用于获取业务服务实例
     * @param signId 签署记录的唯一标识（即电子签全域状态总览表的主键）
     * @throws BOSException 当出现业务异常时抛出，例如：已同步、流程ID为空、员工信息缺失或HTTP请求失败等
     */
    public static void syncAttachmentsForEmpPage(Context ctx, String signId) throws BOSException {
        try {
            IESignGlobalStatusOverview iESignGlobalStatusOverview =
                    ESignGlobalStatusOverviewFactory.getLocalInstance(ctx);
            SelectorItemCollection sic = new SelectorItemCollection();
            sic.add("*");
            // 获取电子签全域状态总览表信息
            ESignGlobalStatusOverviewInfo info = iESignGlobalStatusOverview.getESignGlobalStatusOverviewInfo(new ObjectUuidPK(signId), sic);
            // 判断是否已经同步过附件
            boolean fivouchered = info.isFivouchered();
            if (fivouchered) {
                throw new BOSException(MessageFormat.format("电子签全域状态总览表 [{0}]，签署文件已经同步,请勿重复同步", info.getNumber()));
            }
            // 检查e签宝流程ID是否存在
            String signFlowId = info.getSignFlowId();
            if (StringUtils.isBlank(signFlowId)) {
                throw new BOSException(MessageFormat.format("电子签全域状态总览表 [{0}]，e签宝流程id为空！", info.getNumber()));
            }
            // 检查关联的员工信息是否存在
            PersonInfo person = info.getPerson();
            if (person == null) {
                throw new BOSException(MessageFormat.format("电子签全域状态总览表 [{0}]，员工为空！", info.getNumber()));
            }
            IFileTab iFileTab = FileTabFactory.getLocalInstance(ctx);
            FileTabCollection fileTabCollection = iFileTab.getFileTabCollection("where person.id ='" + person.getId() + "'");
            FileTabInfo fileTabInfo = null;
            if (fileTabCollection.isEmpty()) {
                fileTabInfo = new FileTabInfo();
                fileTabInfo.setPerson(person);
                iFileTab.addnew(fileTabInfo);
            } else {
                fileTabInfo = fileTabCollection.get(0);
            }
            // 调用e签宝接口获取文件下载地址
            EsignHttpResponse response = EsignHttpUtil.getFile_download_url(ctx, signFlowId, 3600, info.getEfileId());
            JSONObject jsonObject = JSON.parseObject(response.getBody());
            Integer code = (Integer) jsonObject.get("code");
            // 处理返回结果并保存文件和附件
            if (code == 0) {
                JSONObject jsonData = jsonObject.getJSONObject("data");
                saveAttachment(ctx, fileTabInfo, jsonData.getJSONArray("files"));
                saveAttachment(ctx, fileTabInfo, jsonData.getJSONArray("attachments"));
            }
            //更新电子签全域状态总览表信息
            info.setFivouchered(true);
            iESignGlobalStatusOverview.updatePartial(info, updateSic);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BOSException(e);
        }
    }


    /**
     * 保存附件
     *
     * @param ctx
     * @param fileTabInfo
     * @throws IOException
     * @throws ShrWebBizException
     * @throws BOSException
     * @throws EASBizException
     */
    private static void saveAttachment(
            Context ctx,
            FileTabInfo fileTabInfo,
            JSONArray attachments
    ) throws IOException, BOSException, EASBizException {
        IESignTemplateFileEntry ieSignTemplateFileEntry = ESignTemplateFileEntryFactory.getLocalInstance(ctx);
        IAttachment iAttachment = AttachmentFactory.getLocalInstance(ctx);
        IBoAttchAsso iBoAttchAsso = BoAttchAssoFactory.getLocalInstance(ctx);
        ISHRAttachmentExt ishrAttachmentExt = SHRAttachmentExtFactory.getLocalInstance(ctx);
        UserInfo userInfo = (UserInfo) ctx.get("UserInfo");
        String userId = userInfo.getId().toString();
        // 遍历并保存签署生成的正式文件
        for (int i = 0; null!=attachments && i < attachments.size(); i++) {
            JSONObject fileMap = attachments.getJSONObject(i);
            String fileName = fileMap.getString("fileName");
            String downloadUrl = fileMap.getString("downloadUrl");
            String fileId = "fileTab" + fileMap.getString("fileId");
            logger.error("fileId: " + fileId);
            if (StringUtils.isBlank(downloadUrl)) {
                logger.error("下载地址为空!");
                continue;
            }
            // 查询对应模板文件的组件ID
            String componentId = ieSignTemplateFileEntry.findByFileName(fileName);
            // 查询附件是否存在,如果存在需要删除
            delAttachment(ctx, fileId);
            byte[] content = DownloaderUtil.downloadFileToByteArray(downloadUrl);
            // 检查文件名是否包含脚本攻击
            fileName = checkScriptAttack(fileName);
            if( fileName.lastIndexOf(46)<=0) {
                fileName +=".pdf";
            }
            String mainname = fileName.substring(0, fileName.lastIndexOf(46));
            String extname = fileName.substring(fileName.lastIndexOf(46) + 1, fileName.length());
            extname = extname.toLowerCase();
            //附件对象
            AttachmentInfo ai = new AttachmentInfo();
            setAttMulNameAndDesc(ctx, ai, mainname, null);
            ai.setSimpleName(extname);
            ai.setFile(content);
            ai.setIsShared(false);
            //ai.setNumber();
            ai.setSharedDesc(SHRWebResource.getString("com.kingdee.shr.base.syssetting.SHRSyssettingResource", "false"));
            int size = content.length;
            if (size < 1024) {
                ai.setSize(size + SHRWebResource.getString("com.kingdee.shr.base.syssetting.SHRSyssettingResource", "byte"));
            } else {
                ai.setSize(size / 1024 + "KB");
            }
            ai.setSizeInByte(size);
            ai.setAttachID(fileId);//附件id
            ai.setType(getFileType(fileName));
            ai.setBeizhu(EMPPAGEUIPK);//uipk
            //附件扩展
            SHRAttachmentExtInfo attchExt = new SHRAttachmentExtInfo();
            attchExt.setAttachment(ai);
            setAttExtMulNameAndDesc(ctx, attchExt, fileName, "");
            attchExt.setPropertyName(componentId);
            attchExt.setType(AttachmentTypeEnum.FORM);
            attchExt.setState(AttachmentState.SAVE);
            String bunding = MessageFormat.format("{0}#{1}", userId, EMPPAGEUIPK);
            attchExt.setBunding(bunding);
            attchExt.setBoID(fileTabInfo.getId().toString());//业务对象id
            try {
                //保存附件对象
                iAttachment.addnew(ai);
            } catch (Exception e) {
                e.printStackTrace();
                throw e;
            }
            attchExt.setState(AttachmentState.SAVE);
            //保存附件扩展
            BoAttchAssoInfo boAttchAssoInfo = new BoAttchAssoInfo();
            boAttchAssoInfo.setBoID(fileTabInfo.getId().toString());//业务对象id
            boAttchAssoInfo.setAssoBusObjType(String.valueOf(fileTabInfo.getBOSType()));
//            boAttchAssoInfo.setAssoType("Added Accessories", LocaleUtils.locale_l1);
//            boAttchAssoInfo.setAssoType("Added Accessories", LocaleUtils.locale_l2);
//            boAttchAssoInfo.setAssoType("Added Accessories", LocaleUtils.locale_l3);
            boAttchAssoInfo.setAttachment(ai);
            //保存
            iBoAttchAsso.addnew(boAttchAssoInfo);
            ishrAttachmentExt.addnew(attchExt);
        }
    }


    /**
     * 删除附件及相关关联信息
     *
     * @param ctx    上下文对象，用于获取服务实例
     * @param fileId 文件ID，标识要删除的附件
     * @throws BOSException    业务对象服务异常
     * @throws EASBizException EAS业务异常
     */
    private static void delAttachment(Context ctx, String fileId) throws BOSException, EASBizException {
        if (StringUtils.isNotBlank(fileId)) {
            IAttachment iAttachment = AttachmentFactory.getLocalInstance(ctx);
            IBoAttchAsso iBoAttchAsso = BoAttchAssoFactory.getLocalInstance(ctx);
            ISHRAttachmentExt ishrAttachmentExt = SHRAttachmentExtFactory.getLocalInstance(ctx);
            FilterInfo filterInfo = new FilterInfo();
            filterInfo.getFilterItems().add(new FilterItemInfo("attachID", fileId));
            EntityViewInfo viewInfo = EntityViewInfo.getInstance(filterInfo, null, null);

            //判断是否存在附件
            if (iAttachment.exists(filterInfo)) {
                // 获取附件集合
                AttachmentCollection attachmentCol = iAttachment.getAttachmentCollection(viewInfo);
                ObjectUuidPK[] attachMentIds = new ObjectUuidPK[attachmentCol.size()];
                Set<String> delIds = new HashSet<String>();

                // 收集需要删除的附件ID
                for (int i = 0; i < attachmentCol.size(); i++) {
                    String id = attachmentCol.get(i).getId().toString();
                    attachMentIds[i] = new ObjectUuidPK(id);
                    delIds.add(id);
                }

                // 构造删除条件并执行删除操作
                FilterInfo deleteFilterInfo = new FilterInfo();
                deleteFilterInfo.getFilterItems().add(new FilterItemInfo("attachment.id", delIds, CompareType.INCLUDE));
                iAttachment.delete(attachMentIds);
                iBoAttchAsso.delete(deleteFilterInfo);
                ishrAttachmentExt.delete(deleteFilterInfo);
            }
        }
    }


    /**
     * 设置附件扩展信息的多语言名称和描述
     *
     * @param ctx  上下文对象，用于获取当前区域设置
     * @param info 附件扩展信息对象，要设置名称和描述的目标对象
     * @param name 附件名称
     * @param desc 附件描述
     */
    private static void setAttExtMulNameAndDesc(Context ctx, SHRAttachmentExtInfo info, String name, String desc) {
        if (info != null && !org.apache.commons.lang3.StringUtils.isEmpty(name)) {
            Locale locale = ctx.getLocale();
            info.setName(name, LocaleUtils.locale_l1);
            info.setDescription(desc, LocaleUtils.locale_l1);

            // 根据当前区域设置确定简体/繁体中文的处理方式
            if (LocaleUtils.locale_l1.getDisplayName().equals(LocaleUtils.getLocaleString(locale))) {
                // 当前区域为locale_l1时，将名称和描述同时设置到locale_l2和locale_l3
                info.setName(name, LocaleUtils.locale_l2);
                info.setName(name, LocaleUtils.locale_l3);
                info.setDescription(desc, LocaleUtils.locale_l2);
                info.setDescription(desc, LocaleUtils.locale_l3);
            } else if (LocaleUtils.locale_l2.getDisplayName().equals(LocaleUtils.getLocaleString(locale))) {
                // 当前区域为locale_l2时，进行简繁转换处理
                info.setName(name, LocaleUtils.locale_l2);
                info.setName(STConverter.sc2tc(name), LocaleUtils.locale_l3);
                if (!org.apache.commons.lang3.StringUtils.isEmpty(desc)) {
                    info.setDescription(desc, LocaleUtils.locale_l2);
                    info.setDescription(STConverter.sc2tc(desc), LocaleUtils.locale_l3);
                }
            } else {
                // 其他情况，进行反向简繁转换处理
                info.setName(STConverter.tc2sc(name), LocaleUtils.locale_l2);
                info.setName(name, LocaleUtils.locale_l3);
                if (!org.apache.commons.lang3.StringUtils.isEmpty(desc)) {
                    info.setDescription(STConverter.tc2sc(desc), LocaleUtils.locale_l2);
                    info.setDescription(desc, LocaleUtils.locale_l3);
                }
            }

        }
    }


    /**
     * 根据文件全名获取文件类型描述
     *
     * @param fullname 文件全名（包含扩展名）
     * @return 文件类型的描述字符串，如果扩展名未知则返回未知类型描述
     */
    private static String getFileType(String fullname) {
        // 提取文件扩展名
        String extname = fullname.substring(fullname.lastIndexOf(46) + 1, fullname.length());

        // 判断是否为Word文档类型
        if (!"doc".equalsIgnoreCase(extname) && !"docx".equalsIgnoreCase(extname)) {
            // 判断是否为Excel文档类型
            if (!"xls".equalsIgnoreCase(extname) && !"xlsx".equalsIgnoreCase(extname) && !"xlsm".equalsIgnoreCase(extname) && !"xlsb".equalsIgnoreCase(extname)) {
                // 判断是否为PowerPoint文档类型
                if (!"ppt".equalsIgnoreCase(extname) && !"pptx".equalsIgnoreCase(extname) && !"pptm".equalsIgnoreCase(extname)) {
                    // 判断是否为文本文件或其他未知类型
                    return "txt".equalsIgnoreCase(extname) ? SHRWebResource.getString("com.kingdee.shr.base.syssetting.SHRSyssettingResource", "text") : MessageFormat.format(SHRWebResource.getString("com.kingdee.shr.base.syssetting.SHRSyssettingResource", "unknow_type"), extname);
                } else {
                    return SHRWebResource.getString("com.kingdee.shr.base.syssetting.SHRSyssettingResource", "microsoft_ppt");
                }
            } else {
                return SHRWebResource.getString("com.kingdee.shr.base.syssetting.SHRSyssettingResource", "microsoft_excel");
            }
        } else {
            return SHRWebResource.getString("com.kingdee.shr.base.syssetting.SHRSyssettingResource", "microsoft_word");
        }
    }

    /**
     * 检查并防止脚本攻击，对输入字符串中的HTML标签符号进行转义处理
     *
     * @param str 待检查和处理的字符串
     * @return 处理后的字符串，将HTML标签符号"<"和">"分别替换为"&lt;"和"&gt;"
     */
    private static String checkScriptAttack(String str) {
        // 检查字符串是否非空
        if (!org.apache.commons.lang3.StringUtils.isEmpty(str)) {
            // 如果包含"<"符号，则进行HTML转义替换
            if (str.contains("<")) {
                str.replaceAll("<", "&lt;");
            }

            // 如果包含">"符号，则进行HTML转义替换
            if (str.contains(">")) {
                str.replaceAll(">", "&gt;");
            }
        }

        return str;
    }

    /**
     * 设置附件的多语言名称和描述信息
     *
     * @param ctx  上下文对象，用于获取多语言环境信息
     * @param att  附件信息对象，需要设置名称和描述的目标对象
     * @param name 附件名称，如果为空则不进行设置
     * @param desc 附件描述信息
     */
    private static void setAttMulNameAndDesc(Context ctx, AttachmentInfo att, String name, String desc) {
        // 当附件对象不为空且名称不为空时，设置附件的多语言名称和描述
        if (att != null && !org.apache.commons.lang3.StringUtils.isEmpty(name)) {
            MutilanUtils.setMultiFieldValueToBean(ctx, "name", att, name);
            //MutilanUtils.setMultiFieldValueToBean(ctx, "description", att, desc);
        }
    }
}
