package org.springblade.flow.engine.custom.type;

import static org.springblade.flow.engine.custom.constants.BasicActivitiConstants.exActivitiPrefixURL;
import static org.springblade.flow.engine.custom.constants.BasicActivitiConstants.extensionAttributeName;
import static org.springblade.flow.engine.custom.constants.GlobalProcessVariableConstants.APPLY_USER_ID;
import static org.springblade.flow.engine.custom.constants.GlobalProcessVariableConstants.BILL_CODE;
import static org.springblade.flow.engine.custom.constants.GlobalProcessVariableConstants.BUSINESS_TITLE;
import static org.springblade.flow.engine.custom.constants.GlobalProcessVariableConstants.FLOW_ENGINE_ID;
import static org.springblade.flow.engine.custom.constants.GlobalProcessVariableConstants.PROCESS_CATEGORY;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.ManualTask;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.ReceiveTask;
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.event.impl.FlowableActivityEventImpl;
import org.flowable.engine.impl.bpmn.behavior.ReceiveTaskActivityBehavior;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.SimpleHtmlSerializer;
import org.htmlcleaner.TagNode;
import org.springblade.flow.base.util.ActivitiUtils;
import org.springblade.flow.base.util.BpmnModelUtils;
import org.springblade.flow.base.util.SpringContextHolder;
import org.springblade.flow.config.ConfigValueUtil;
import org.springblade.flow.engine.assembly.ManualTaskExPropertyDTO;
import org.springblade.flow.engine.assembly.ReceiveTaskExPropertyDTO;
import org.springblade.flow.engine.custom.behavior.impl.CustomManualTaskActivityBehavior;
import org.springblade.flow.engine.custom.behavior.impl.CustomParallelMultiInstanceBehavior;
import org.springblade.flow.engine.custom.behavior.impl.CustomSequentialMultiInstanceBehavior;
import org.springblade.flow.engine.custom.constants.BasicActivitiConstants;
import org.springblade.flow.engine.custom.helper.UserTaskNodeHelper;
import org.springblade.flow.engine.dto.MailFileInfo;
import org.springblade.flow.engine.entity.FlowTaskCirculateEntity;
import org.springblade.flow.engine.enums.FlowMessageEnum;
import org.springblade.flow.engine.enums.FlowNodeTypeEnum;
import org.springblade.flow.engine.service.FlowTaskCirculateService;
import org.springblade.flow.exception.WorkFlowException;
import org.springblade.flow.message.enums.MessageTypeEnum;
import org.springblade.flow.message.model.message.SentMessageForm;
import org.springblade.flow.message.util.SentMessageUtil;
import org.springblade.flow.permission.entity.UserEntity;
import org.springblade.flow.permission.entity.UserRelationEntity;
import org.springblade.flow.permission.service.User4FlowService;
import org.springblade.flow.permission.service.UserRelationService;
import org.springblade.flow.util.JsonUtil;
import org.springblade.flow.util.JsonUtilEx;
import org.springblade.flow.util.RandomUtil;
import org.springblade.flow.util.RedisUtil;
import org.springblade.flow.util.enums.FileTypeEnum;
import org.springblade.flow.util.file.UploadUtil;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;

/**
 * @author LoveMyOrange
 * @create 2022-01-11 21:32
 * @desc 用于ReceiveTask 时长给Redis 放key
 */
@Component
public class ActivityStartedListener implements FlowableEventListener {
    @Override
    public void onEvent(FlowableEvent event) {
        ActivitiUtils activitiUtils = SpringContextHolder.getBean(ActivitiUtils.class);
        UserRelationService userRelationService = SpringContextHolder.getBean(UserRelationService.class);
        FlowTaskCirculateService flowTaskCirculateService = SpringContextHolder.getBean(FlowTaskCirculateService.class);
        SentMessageUtil sentMessageUtil = SpringContextHolder.getBean(SentMessageUtil.class);
        UserTaskNodeHelper userTaskNodeHelper = SpringContextHolder.getBean(UserTaskNodeHelper.class);

        if(event instanceof FlowableActivityEventImpl){
            FlowableActivityEventImpl activityEvent =(FlowableActivityEventImpl)event;
            String activityId = activityEvent.getActivityId();
            String executionId = activityEvent.getExecutionId();
            String activityType = activityEvent.getActivityType();
            String behaviorClass = activityEvent.getBehaviorClass();
            String processDefinitionId = activityEvent.getProcessDefinitionId();
            String processInstanceId = activityEvent.getProcessInstanceId();
            DelegateExecution execution = activityEvent.getExecution();

            User4FlowService usersApi = SpringContextHolder.getBean(User4FlowService.class);
            //此事件需要提前为会签节点分配变量
            if(CustomParallelMultiInstanceBehavior.class.getName().equals(behaviorClass) ||
                    CustomSequentialMultiInstanceBehavior.class.getName().equals(behaviorClass)){
            }
            //接收任务->目前应用于 定时器
            else if(ReceiveTaskActivityBehavior.class.getName().equals(behaviorClass)){
                //目前只判断是不是接收任务
                    //

                    RedisUtil redisUtil = SpringContextHolder.getBean(RedisUtil.class);
                    Process process = BpmnModelUtils.getBpmnModelMainProcess(processDefinitionId);
                    ReceiveTask activity = (ReceiveTask) process.getFlowElement(activityId);
                    String property = activity.getAttributeValue(exActivitiPrefixURL, extensionAttributeName);
                    ReceiveTaskExPropertyDTO activityExPropertyDTO = JSONObject.parseObject(property, new TypeReference<ReceiveTaskExPropertyDTO>() {
                    });
                    Integer totalSeconds = activityExPropertyDTO.getTotalSeconds();
                    if(totalSeconds!=null){
                        String key=BasicActivitiConstants.RECEIVE_TASK_TIME_KEY_PREFIX+"-"+processInstanceId+"-"+executionId+"-"+activityId;
                        //
                        redisUtil.insert(key,true,totalSeconds);
                    }
            }
            //手工任务->目前应用于 抄送节点->邮件节点->发送钉钉\站内信\微信\短信
            else if(CustomManualTaskActivityBehavior.class.getName().equals(behaviorClass)){
                String dirProp = System.getProperty("user.dir");
                Process process = BpmnModelUtils.getBpmnModelMainProcess(processDefinitionId);
                ManualTask activity = (ManualTask) process.getFlowElement(activityId);
                String property = activity.getAttributeValue(exActivitiPrefixURL, extensionAttributeName);
                ManualTaskExPropertyDTO manualTaskExPropertyDTO = JSONObject.parseObject(property, new TypeReference<ManualTaskExPropertyDTO>() {
                });
                String type = manualTaskExPropertyDTO.getType();
                //如果是抄送节点
                if(FlowNodeTypeEnum.COPY.getType().equals(type)){
                    //1.2  处理传阅数据
                    List<String> circulatePosition = manualTaskExPropertyDTO.getCirculatePosition();
                    List<String> circulateUsers = manualTaskExPropertyDTO.getCirculateUser();
                    List<String> circulateRole = manualTaskExPropertyDTO.getCirculateRole();

                    List<String> circulateUserIds=new ArrayList<>();
                    List<String> userAll = new ArrayList<>();
                    if(circulatePosition!=null){
                        userAll.addAll(circulatePosition);
                    }
                    if(circulateRole!=null){
                        userAll.addAll(circulateRole);
                    }

                    if(userAll.size()>0){
                        List<UserRelationEntity> listByObjectIdAll = userRelationService.getListByObjectIdAll(userAll);
                        if(listByObjectIdAll!=null){
                            for (UserRelationEntity userRelationEntity : listByObjectIdAll) {
                                circulateUserIds.add(userRelationEntity.getUserId());
                            }
                        }
                    }

                    if(circulateUsers!=null){
                        circulateUserIds.addAll(circulateUsers);
                    }


                    ArrayList<String> distinctCirUserIds=new ArrayList<>();
                    if(circulateUserIds.size()>0){
                        distinctCirUserIds= CollUtil.distinct(circulateUserIds);
                        List<FlowTaskCirculateEntity> flowTaskCirculateEntityList = new ArrayList<>();
                        for (String userId : distinctCirUserIds) {
                            Map<String,String> paramsMap=new HashMap<>();
                            paramsMap.put("taskDefinitionKey",activity.getId());
                            paramsMap.put("taskName",activity.getName());
//                            paramsMap.put("taskId",)
                            paramsMap.put("processInstanceId",processInstanceId);
                            operateCirculateInfo(paramsMap,flowTaskCirculateEntityList,userId,execution);
                        }

                        //1.传阅的批量数据插入
                        if (!flowTaskCirculateEntityList.isEmpty()) {
                            flowTaskCirculateService.create(flowTaskCirculateEntityList);
                        }
                        //1.4 消息推送(站内信)
                        //审核提醒
                        String fullName = execution.getVariable(BUSINESS_TITLE, String.class);
                        List<String> messageType=new ArrayList<>();
                        messageType.add(MessageTypeEnum.SysMessage.getCode());
                        SentMessageForm sentMessageForm = this.message("【抄送】", FlowMessageEnum.circulate.getCode(), messageType, processInstanceId, distinctCirUserIds,fullName);
                        sentMessageUtil.sendMessage(sentMessageForm);
                    }

                }
                //如果是抄送节点
                else if(FlowNodeTypeEnum.MAIL.getType().equals(type)){

                    JavaMailSender javaMailSender = SpringContextHolder.getBean(JavaMailSender.class);
                    ConfigValueUtil configValueUtil = SpringContextHolder.getBean(ConfigValueUtil.class);
                    MimeMessage mimeMessage = javaMailSender.createMimeMessage();
                    MimeMessageHelper helper= null;
                    try {
                        helper = new MimeMessageHelper(mimeMessage,true, Constants.UTF_8);
                    } catch (MessagingException e) {
                        e.printStackTrace();
                        throw new WorkFlowException("发送邮件失败");
                    }


                    List<String> to = manualTaskExPropertyDTO.getTo();
                    String from = manualTaskExPropertyDTO.getFrom();
                    List<String> cc = manualTaskExPropertyDTO.getCc();
                    List<String> bcc = manualTaskExPropertyDTO.getBcc();
                    String subject = manualTaskExPropertyDTO.getSubject();
                    String html = manualTaskExPropertyDTO.getHtml();
                    List<MailFileInfo> mailFiles = manualTaskExPropertyDTO.getMailFiles();


                    Set<String> userIds=new HashSet<>();
                    userIds.addAll(to);
                    userIds.addAll(cc);
                    userIds.addAll(bcc);


                    //
                    if(CollUtil.isNotEmpty(userIds)){
                        LambdaQueryWrapper<UserEntity> userEntityQueryWrapper=new LambdaQueryWrapper<>();
                        userEntityQueryWrapper.in(UserEntity::getId,userIds);
                        List<UserEntity> list = usersApi.list(userEntityQueryWrapper);

                        List<String> emailTo=new ArrayList<>();
                        List<String> emailCc=new ArrayList<>();
                        List<String> emailBcc=new ArrayList<>();

                        for (UserEntity userEntity : list) {
                            String email = userEntity.getEmail();
                            String id = userEntity.getId();
                            if(StringUtils.isNotBlank(email)){
                                if(to.contains(id)){
                                    emailTo.add(email);
                                }
                                else if(cc.contains(id)){
                                    emailCc.add(email);
                                }
                                else if(bcc.contains(id)){
                                    emailBcc.add(id);
                                }
                            }
                        }

                        //如果都是空,那么就不应该进行发邮件的后续工作
                        if(emailTo.isEmpty() && emailCc.isEmpty() && emailBcc.isEmpty()){
                            return;
                        }

                        List<File> fileList=new ArrayList<>();
                        try{

                            helper.setFrom("management@lovemyorangeforever.com");
                            if(!emailTo.isEmpty()){
                                helper.setTo(emailTo.toArray(new String[emailTo.size()]));
                            }
                            if(!emailCc.isEmpty()){
                                helper.setCc(emailCc.toArray(new String[emailCc.size()]));
                            }
                            if(emailBcc.isEmpty()){
                                helper.setBcc(emailBcc.toArray(new String[emailBcc.size()]));
                            }
                            helper.setSubject(subject);
                            //为何使用此工具? 详情参见 JavaMail邮件指南
                            HtmlCleaner htmlCleaner=new HtmlCleaner();
                            TagNode tagNode = htmlCleaner.clean(html);
                            //找到所有的image标签全局替换
                            Object[] objects = tagNode.evaluateXPath("//img");
                            List<Object> elements = Arrays.asList(objects);
                            //估计是JavaMail的bug....... 巨坑
                            Map<String, File> fileMap=new HashMap<>();

                           fileList=new ArrayList<>();

                            for (Object element : elements) {
                                TagNode imgTagNode=(TagNode)element;
                                String src = imgTagNode.getAttributeByName("src");
                                String base64=";base64,";
                                String data="data:image/";
                                int i = src.indexOf(base64);
                                //1.1获取图片类型
                                String suffix = src.substring(data.length(), i);
                                //1.2获取图片的base64
                                i=i+base64.length();
                                String substring = src.substring(i, src.length() - 2);
                                byte[] decode = Base64.decode(substring);
                                String fileName = cn.hutool.core.util.RandomUtil.randomString(10);
                                File tempFile = new File( dirProp+File.separator+fileName + "." + suffix);
                                FileUtil.writeBytes(decode,tempFile);
                                Map<String,String> attr=new HashMap<>();
                                attr.put("src","cid:"+fileName);
                                imgTagNode.setAttributes(attr);
                                fileMap.put(fileName,tempFile);
                                fileList.add(tempFile);
                            }

                            SimpleHtmlSerializer serializer = new SimpleHtmlSerializer(htmlCleaner.getProperties());
                            File tempFile = new File(dirProp+File.separator+ cn.hutool.core.util.RandomUtil.randomString(9));
                            serializer.writeToFile(tagNode,tempFile.getAbsolutePath());
                            String finalStr = FileUtil.readString(tempFile, "UTF-8");
                            helper.setText(finalStr,true);
                            fileList.add(tempFile);
                            //addInline
                            Set<String> set = fileMap.keySet();
                            for (String s : set) {
                                helper.addInline(s,fileMap.get(s));
                            }


                            //添加附件
                            String temporaryFile = configValueUtil.getTemporaryFilePath();
                            String mailFilePath = configValueUtil.getEmailFilePath();
                            if(mailFiles!=null){
                                for (MailFileInfo mailFile : mailFiles) {
                                    UploadUtil.copyObject(configValueUtil.getFileType(), temporaryFile, mailFile.getFileId(), mailFilePath, mailFile.getFileId());
                                }

                                if (mailFiles.size() > 0) {
                                    for (MailFileInfo mailFile : mailFiles) {
                                        UploadUtil.downToLocal(type, mailFile.getFileId(), FileTypeEnum.MAIL, mailFilePath);
                                        File file = new File(mailFilePath + mailFile.getFileId());
                                        String name = mailFile.getName();
                                        helper.addAttachment(name, file);
                                    }
                                }
                            }


                            javaMailSender.send(mimeMessage);
                        }
                        catch (Exception e){
                            e.printStackTrace();
                            throw new WorkFlowException("发送邮件失败");
                        }
                        finally {
                            if(fileList.size()>0){
                                for (File file : fileList) {
                                    if(file!=null){
                                        file.delete();
                                    }
                                }
                            }
                        }


                    }
                }
                //
                else if(FlowNodeTypeEnum.ZHAN.getType().equals(type)){
                    List<String> to = manualTaskExPropertyDTO.getTo();
                    if(CollUtil.isNotEmpty(to)){
                        List<FlowTaskCirculateEntity> flowTaskCirculateEntityList = new ArrayList<>();
                        String notifyContent = manualTaskExPropertyDTO.getNotifyContent();
                        Boolean processFlag = manualTaskExPropertyDTO.getProcessFlag();
                        SentMessageForm sentMessageForm =new SentMessageForm();
                        List<String> messageType=new ArrayList<>();
                        messageType.add(MessageTypeEnum.SysMessage.getCode());
                        sentMessageForm.setSendType(messageType);
                        sentMessageForm.setContent(notifyContent);
                        sentMessageForm.setTitle("测试站内信标题");
                        sentMessageForm.setToUserIds(to);
                        sentMessageUtil.sendMessage(sentMessageForm);
                        if(processFlag){
                            for (String userId : to) {
                                Map<String,String> paramsMap=new HashMap<>();
                                paramsMap.put("taskDefinitionKey",activity.getId());
                                paramsMap.put("taskName",activity.getName());
//                            paramsMap.put("taskId",)
                                paramsMap.put("processInstanceId",processInstanceId);
                                operateCirculateInfo(paramsMap,flowTaskCirculateEntityList,userId,execution);
                            }
                            //1.传阅的批量数据插入
                            if (!flowTaskCirculateEntityList.isEmpty()) {
                                flowTaskCirculateService.create(flowTaskCirculateEntityList);
                            }
                        }
                    }

                }

                else if(FlowNodeTypeEnum.DX.getType().equals(type)){

                }
                else if(FlowNodeTypeEnum.WX.getType().equals(type)){

                }
                else if(FlowNodeTypeEnum.DING.getType().equals(type)){

                }


            }
        }
    }

    @Override
    public boolean isFailOnException() {
        return true;
    }

    @Override
    public boolean isFireOnTransactionLifecycleEvent() {
        return false;
    }

    @Override
    public String getOnTransaction() {
        return null;
    }


    private RepositoryService getRepositoryService() {
        return SpringContextHolder.getBean(RepositoryService.class);
    }



    /**
     * 处理传阅信息
     * @param flowTaskOperatorEntityList
     */
    private void operateCirculateInfo(Map<String,String> map, List<FlowTaskCirculateEntity> flowTaskOperatorEntityList, String type,DelegateExecution delegateExecution){
        String taskDefinitionKey = MapUtil.getStr(map, "taskDefinitionKey");
        String taskName = MapUtil.getStr(map, "taskName");
        String taskId = MapUtil.getStr(map, "taskId");
        String processInstanceId = MapUtil.getStr(map, "processInstanceId");
        FlowTaskCirculateEntity flowTask = new FlowTaskCirculateEntity();
        flowTask.setId(RandomUtil.uuId());
        flowTask.setObjectId(type);
        flowTask.setNodeCode(taskDefinitionKey);
        flowTask.setNodeName(taskName);
        if(StringUtils.isNotBlank(taskId)){
            flowTask.setTaskNodeId(taskId);
        }
        flowTask.setTaskId(processInstanceId);
        flowTask.setCreatorTime(new Date());
        Map<String, Object> variables = delegateExecution.getVariables();
        flowTask.setEnCode(MapUtil.getStr(variables,BILL_CODE));
        flowTask.setFullName(MapUtil.getStr(variables,BUSINESS_TITLE));
        flowTask.setFlowId(MapUtil.getStr(variables,FLOW_ENGINE_ID));
        flowTask.setFlowCategory(MapUtil.getStr(variables,PROCESS_CATEGORY));
        flowTask.setUpdateTime(new Date());
        flowTask.setCreateUserId(MapUtil.getStr(variables,APPLY_USER_ID));
        flowTaskOperatorEntityList.add(flowTask);
    }


    /**
     * 获取HttpServletRequest
     * @return
     */
    private static HttpServletRequest getRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        return request;
    }


    /**
     * 发送消息封装
     *
     * @param title       名称
     * @param type        类型
     * @param messageType 发送消息类型
     * @param userList    发送用户
     * @return
     */
    private SentMessageForm message(String title, int type, List<String> messageType, String processInstanceId, List<String> userList,String fullName) {
        SentMessageForm sentMessageForm = new SentMessageForm();
        sentMessageForm.setSendType(messageType);
        Map<String, Object> message = new HashMap<>(16);
        message.put("type", type);
        message.put("id", processInstanceId);
        sentMessageForm.setContent(JsonUtilEx.getObjectToString(message));
        sentMessageForm.setTitle(fullName + title);
        sentMessageForm.setToUserIds(userList);
        Map<String, String> smsContent = new HashMap<>(16);
        this.message(fullName, title, smsContent);
        sentMessageForm.setSmsContent(smsContent);
        return sentMessageForm;
    }

    /**
     * 短信模板
     *
     * @param title  标题
     * @param status 状态
     * @return
     */
    private void message(String title, String status, Map<String, String> msg) {
        Map<String, String> aliyun = new HashMap<>(16);
        aliyun.put("title", title);
        aliyun.put("status", status);
        msg.put("1", JsonUtil.getObjectToString(aliyun));
        msg.put("2", "2");
    }

}
