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

import static org.springblade.flow.base.util.BpmnModelUtils.getAttributeValueByTaskDefinitionKey;
import static org.springblade.flow.base.util.BpmnModelUtils.getBpmnModelMainProcess;
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 static org.springblade.flow.engine.custom.constants.RootExecutionVariableConstants.MULTI_INSTANCE_FIRST_FLAG;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
import org.flowable.common.engine.impl.event.FlowableEntityEventImpl;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.impl.RepositoryServiceImpl;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springblade.flow.base.util.ActivitiNativeTaskQueryUtils;
import org.springblade.flow.base.util.SpringContextHolder;
import org.springblade.flow.engine.assembly.UserTaskExPropertyDTO;
import org.springblade.flow.engine.assembly.prop.UserTaskAdvancedSettingDTO;
import org.springblade.flow.engine.assembly.prop.UserTaskBasicPropDTO;
import org.springblade.flow.engine.custom.cmd.execution.GetRootExecutionEntityCmd;
import org.springblade.flow.engine.custom.constants.BasicActivitiConstants;
import org.springblade.flow.engine.custom.constants.GlobalProcessVariableConstants;
import org.springblade.flow.engine.custom.helper.UserTaskNodeHelper;
import org.springblade.flow.engine.entity.FlowTaskCirculateEntity;
import org.springblade.flow.engine.entity.FlowTaskOperatorEntity;
import org.springblade.flow.engine.enums.FlowMessageEnum;
import org.springblade.flow.engine.enums.FlowNodeEnum;
import org.springblade.flow.engine.model.flowengine.shuntjson.childnode.TimeOutConfig;
import org.springblade.flow.engine.service.FlowTaskCirculateService;
import org.springblade.flow.engine.service.FlowTaskOperatorRecordService;
import org.springblade.flow.engine.service.FlowTaskOperatorService;
import org.springblade.flow.exception.WorkFlowException;
import org.springblade.flow.permission.entity.UserEntity;
import org.springblade.flow.permission.entity.UserRelationEntity;
import org.springblade.flow.permission.service.UserRelationService;
import org.springblade.flow.permission.service.User4FlowService;
import org.springblade.flow.util.RandomUtil;
import org.springblade.flow.util.RedisUtil;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;


/**
 * @Author:LoveMyOrange
 * @Description: 任务创建事件 初始化
 * @Date:Created in 2021/12/30 21:54
 */
@Component
@Slf4j
public class TaskCreatedInitListener implements FlowableEventListener {


    @Override
    public void onEvent(FlowableEvent activitiEvent) {
        if (!(activitiEvent instanceof FlowableEntityEventImpl)) {
            return;
        }

        FlowableEntityEventImpl activitiEntityEventImpl = (FlowableEntityEventImpl) activitiEvent;
        String executionId = activitiEntityEventImpl.getExecutionId();
        Object entity = activitiEntityEventImpl.getEntity();

        if (!(entity instanceof TaskEntity)) {
            return;
        }
        TaskEntity taskEntity = (TaskEntity) entity;
        User4FlowService usersApi = SpringContextHolder.getBean(User4FlowService.class);
        FlowTaskOperatorRecordService flowTaskOperatorRecordService = SpringContextHolder.getBean(FlowTaskOperatorRecordService.class);
        UserTaskNodeHelper userTaskNodeHelper = SpringContextHolder.getBean(UserTaskNodeHelper.class);
        ManagementService managementService = SpringContextHolder.getBean(ManagementService.class);
        FlowTaskCirculateService flowTaskCirculateService = SpringContextHolder.getBean(FlowTaskCirculateService.class);
        UserRelationService userRelationService = SpringContextHolder.getBean(UserRelationService.class);
        FlowTaskOperatorService flowTaskOperatorService = SpringContextHolder.getBean(FlowTaskOperatorService.class);
        RedisUtil redisUtil = SpringContextHolder.getBean(RedisUtil.class);

        String processDefinitionId = taskEntity.getProcessDefinitionId();
        String taskDefinitionKey = taskEntity.getTaskDefinitionKey();
        String processInstanceId = taskEntity.getProcessInstanceId();
        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
        ExecutionEntity rootExecutionEntity = executionEntityManager.findById(processInstanceId);
        String businessKey = rootExecutionEntity.getProcessInstanceBusinessKey();
        String applyUserId = (String) rootExecutionEntity.getVariable(BasicActivitiConstants.startEventInitiator);
        //
        ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)
                ((RepositoryServiceImpl) getRepositoryService())
                        .getDeployedProcessDefinition(processDefinitionId);
        UserEntity info = usersApi.getById(applyUserId);
        Process mainProcess = getRepositoryService().getBpmnModel(processDefinitionId).getMainProcess();
        UserTask userTask = (UserTask) mainProcess.getFlowElement(taskDefinitionKey);
        String property = getAttributeValueByTaskDefinitionKey(mainProcess, taskDefinitionKey, "userTask");
        UserTaskExPropertyDTO  exPropertyDTO = JSONObject.parseObject(property, new TypeReference<UserTaskExPropertyDTO>(){}) ;


        UserTaskBasicPropDTO userTaskBasicPropDTO = exPropertyDTO.getUserTaskBasicPropDTO();
        taskEntity.setCategory(processDefinitionEntity.getCategory());//设置任务分类
        taskEntity.setFormKey(businessKey);//formKey 存储 businessKey 的话 可以少查一次流程实例表 查businessKey 或者变量表查一次

        //设置超时信息
        UserTaskAdvancedSettingDTO userTaskAdvancedSettingDTO = exPropertyDTO.getUserTaskAdvancedSettingDTO();
        TimeOutConfig timeOutConfig = userTaskAdvancedSettingDTO.getTimeOutConfig();
        if(timeOutConfig!=null){
            Date createTime = taskEntity.getCreateTime();

            Boolean on = timeOutConfig.getOn();
            Integer quantity = timeOutConfig.getQuantity();
            String type = timeOutConfig.getType();
            Integer finalSeconds=null;
            if(on){
                //天
                if("day".equals(type)){
                    finalSeconds=  quantity*24*60*60;
                }
                else if("hour".equals(type)){
                    finalSeconds=quantity*60*60;
                }
                else{
                    finalSeconds=quantity*60;
                }
                //秒->
                if(finalSeconds!=null){
                    Integer millSeconds=finalSeconds*1000;
                    createTime.setTime(createTime.getTime()+millSeconds);
                    taskEntity.setDueDate(createTime);
                    //向redis设置一个key,这样当超时之后 即可设置
                    String redisKey=BasicActivitiConstants.USER_TASK_TIME_KEY_PREFIX
                            +"-"+taskEntity.getProcessInstanceId()
                            +"-"+taskEntity.getId()
                            +"-"+taskDefinitionKey
                            +"-"+taskEntity.getProcessDefinitionId();
                    redisUtil.insert(redisKey,true,finalSeconds);
                }
            }
        }


        String businessTitle = rootExecutionEntity.getVariable(GlobalProcessVariableConstants.BUSINESS_TITLE, String.class);


        //经办记录的List
        List<FlowTaskOperatorEntity> flowTaskOperatorEntityList = new ArrayList<>();
        //消息提醒
        Map<String, Object> message = new HashMap<>(16);
        message.put("type", FlowMessageEnum.wait.getCode());
        message.put("id", taskEntity.getProcessInstanceId());
        Process process = getBpmnModelMainProcess(processDefinitionId);
        FlowElement destination = ((StartEvent) process.getInitialFlowElement()).getOutgoingFlows().get(0).getTargetFlowElement();
        //当前节点是发起人节点
        if (taskDefinitionKey.equals(destination.getId())) {
            taskEntity.setClaimTime(new Date());
            taskEntity.setAssignee(applyUserId);
            //
            operateSimpleUserTask(exPropertyDTO,applyUserId,taskEntity,flowTaskOperatorEntityList);

            //判断是不是第一次进入该节点, 如果不是则肯定是驳回回来的,那么 驳回回来的是需要发站内信的,
            ActivitiNativeTaskQueryUtils activitiNativeTaskQueryUtils = SpringContextHolder.getBean(ActivitiNativeTaskQueryUtils.class);
            Long count = activitiNativeTaskQueryUtils.firstActivityFlag(taskDefinitionKey, taskEntity.getProcessInstanceId());
            //代表已经出现过很多此了, 需要站内信提醒
            if(count>1){
                List<String> assigneeUserIds=new ArrayList<>();
                assigneeUserIds.add(applyUserId);
                //审核提醒
                userTaskNodeHelper.message(exPropertyDTO,null,taskEntity,assigneeUserIds,businessTitle,null );
            }
            else{
                //第一次 插入 record

            }

        }
        //审批节点
        else {
            String flowId = rootExecutionEntity.getVariable(FLOW_ENGINE_ID).toString();
            String assigneeType = userTaskBasicPropDTO.getAssigneeType();
            List<String> assignUserList = userTaskNodeHelper.getAssignUserList(exPropertyDTO, taskEntity, flowId);
            if(assignUserList.size()==0){
                throw new WorkFlowException("节点上没有找到审批人,请检查配置");
            }
            boolean hasMultiInstanceLoopCharacteristics = userTask.hasMultiInstanceLoopCharacteristics();
            if(hasMultiInstanceLoopCharacteristics){
                ExecutionEntity executionEntity = managementService.executeCommand(new GetRootExecutionEntityCmd(taskEntity.getExecutionId(), taskDefinitionKey));
                String variableName = taskDefinitionKey + "assigneeList";
                boolean b = executionEntity.hasVariableLocal(taskDefinitionKey+MULTI_INSTANCE_FIRST_FLAG);
                if (b) {
                    taskEntity.setClaimTime(new Date());
                    //Flowable历史表不会更新hsitory相关信息
                    CommandContextUtil.getHistoryManager().recordTaskInfoChange(taskEntity,taskDefinitionKey,new Date());
                    //代表是第二次进入会签节点
                    return;//直接return
                } else {
                    executionEntity.setVariableLocal(taskDefinitionKey+MULTI_INSTANCE_FIRST_FLAG,true);
                    taskEntity.setClaimTime(new Date());


                    //审核提醒
                    userTaskNodeHelper.message
                            (exPropertyDTO,null,taskEntity,
                                    assignUserList,businessTitle , null);



//                    List<? extends ExecutionEntity> executions = executionEntity.getExecutions();
                    //
//                    operateHqUserTask(exPropertyDTO,executions,flowTaskOperatorEntityList);
                }
            }
            else{
                for (String assigneeUser : assignUserList) {
                    operateSimpleUserTask(exPropertyDTO,assigneeUser,taskEntity,flowTaskOperatorEntityList);
                }
                //分配审批人即可
                if(assignUserList.size()==1){
                    taskEntity.setClaimTime(new Date());
                    taskEntity.setAssignee(assignUserList.get(0));
                }
                else{
                    taskEntity.addCandidateUsers(assignUserList);
                }

                //审核提醒
                userTaskNodeHelper.message
                        (exPropertyDTO,null,taskEntity,
                                assignUserList,businessTitle , null);

            }


            //关于这里的事件 应该到 点审批的时候才 触发(比如 同意 拒绝)


            //1.2  处理传阅数据
            List<String> circulatePosition = userTaskBasicPropDTO.getCirculatePosition();
            List<String> circulateUsers = userTaskBasicPropDTO.getCirculateUser();
            List<String> circulateRole = userTaskBasicPropDTO.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) {
                    operateCirculateInfo(taskEntity,flowTaskCirculateEntityList,userId);
                }

                //1.传阅的批量数据插入
                if (!flowTaskCirculateEntityList.isEmpty()) {
                    flowTaskCirculateService.create(flowTaskCirculateEntityList);
                }
                //1.4 消息推送(站内信)
                //抄送提醒
                userTaskNodeHelper.message(exPropertyDTO,distinctCirUserIds,taskEntity,assignUserList,businessTitle , null);
            }

        }


        //Flowable历史表不会更新hsitory相关信息
        CommandContextUtil.getHistoryManager().recordTaskInfoChange(taskEntity,taskDefinitionKey,new Date());


        //插入经办记录
        if (!flowTaskOperatorEntityList.isEmpty()) {

            flowTaskOperatorService.create(flowTaskOperatorEntityList);
        }
    }

    @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 positionAll  岗位数据
     * @param userPosition 用户岗位数据
     * @return
     */
    private boolean getCirculateUser(List<String> positionAll, List<String> userPosition) {
        boolean flag = true;
        for (String positionId : positionAll) {
            if (userPosition.stream().filter(t -> t.contains(positionId)).count() > 0) {
                flag = false;
                break;
            }
        }
        return flag;
    }





    /**
     *
     * @param userTaskExPropertyDTO
     * @param userId
     * @param taskEntity
     * @param flowTaskOperatorEntityList
     */
    private void operateSimpleUserTask(UserTaskExPropertyDTO userTaskExPropertyDTO, String userId, TaskEntity taskEntity, List<FlowTaskOperatorEntity> flowTaskOperatorEntityList){
        UserTaskBasicPropDTO userTaskBasicPropDTO = userTaskExPropertyDTO.getUserTaskBasicPropDTO();
        UserTaskAdvancedSettingDTO userTaskAdvancedSettingDTO = userTaskExPropertyDTO.getUserTaskAdvancedSettingDTO();
        String description = userTaskAdvancedSettingDTO.getDescription();
        String type = userTaskBasicPropDTO.getAssigneeType();
        FlowTaskOperatorEntity operator = new FlowTaskOperatorEntity();
        operator.setId(RandomUtil.uuId());
        operator.setHandleType(type);
        operator.setHandleId(userId);
        operator.setTaskNodeId(taskEntity.getId());
        operator.setTaskId(taskEntity.getProcessInstanceId());
        operator.setNodeCode(taskEntity.getTaskDefinitionKey());
        operator.setNodeName(taskEntity.getName());
        operator.setDescription(description);
        operator.setCreatorTime(new Date());
        String progress = userTaskBasicPropDTO.getProgress();
        if(StringUtils.isNotBlank(progress)){
            operator.setCompletion(Integer.valueOf(progress));
        }
        else{
            operator.setCompletion(0);
        }
        operator.setType(type);
        operator.setState(FlowNodeEnum.Process.getCode());
        flowTaskOperatorEntityList.add(operator);
    }


    /**
     * 处理传阅信息
     * @param flowTaskOperatorEntityList
     */
    private void operateCirculateInfo(TaskEntity taskEntity,List<FlowTaskCirculateEntity> flowTaskOperatorEntityList,String type){
        FlowTaskCirculateEntity flowTask = new FlowTaskCirculateEntity();
        flowTask.setId(RandomUtil.uuId());
        flowTask.setObjectId(type);
        flowTask.setNodeCode(taskEntity.getTaskDefinitionKey());
        flowTask.setNodeName(taskEntity.getName());
        flowTask.setTaskNodeId(taskEntity.getId());
        flowTask.setTaskId(taskEntity.getProcessInstanceId());
        flowTask.setCreatorTime(new Date());
        Map<String, Object> variables = taskEntity.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);
    }


}
