package org.springblade.flow.engine.custom.behavior.impl;

import static org.springblade.flow.base.util.BpmnModelUtils.getAttributeValueByTaskDefinitionKey;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.common.engine.api.scope.ScopeTypes;
import org.flowable.common.engine.impl.el.ExpressionManager;
import org.flowable.common.engine.impl.logging.LoggingSessionConstants;
import org.flowable.common.engine.impl.logging.LoggingSessionUtil;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior;
import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.BpmnLoggingSessionUtil;
import org.flowable.engine.impl.util.IdentityLinkUtil;
import org.flowable.engine.impl.util.TaskHelper;
import org.flowable.identitylink.service.impl.persistence.entity.IdentityLinkEntity;
import org.flowable.task.api.Task;
import org.flowable.task.service.TaskService;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
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.entity.FlowTaskOperatorEntity;
import org.springblade.flow.engine.enums.FlowNodeEnum;
import org.springblade.flow.engine.service.FlowTaskOperatorService;
import org.springblade.flow.util.RandomUtil;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.fasterxml.jackson.databind.node.ObjectNode;

import lombok.extern.slf4j.Slf4j;

/**
 * 此类不能注入到Spring容器中  因为行为类实现类不可以是单例的
 * 自定义行为可以监控到各个元素的各种行为, 只需要覆盖相应方法即可
 */
@Slf4j
public class CustomUserTaskActivityBehavior extends UserTaskActivityBehavior {
    public CustomUserTaskActivityBehavior(UserTask userTask) {
        super(userTask);
    }

    /**
     *  分配人  其实也可以在此方法中 添加审批人
     * @param taskService
     * @param assignee
     * @param owner
     * @param candidateUsers
     * @param candidateGroups
     * @param task
     * @param expressionManager
     * @param execution
     * @param processEngineConfiguration
     */
    @Override
    protected void handleAssignments(TaskService taskService, String assignee, String owner, List<String> candidateUsers,
                                     List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager, DelegateExecution execution,
                                     ProcessEngineConfigurationImpl processEngineConfiguration) {
        if (StringUtils.isNotEmpty(assignee)) {
            Object assigneeExpressionValue = expressionManager.createExpression(assignee).getValue(execution);
            String assigneeValue = null;
            if (assigneeExpressionValue != null) {
                assigneeValue = assigneeExpressionValue.toString();
            }

            if (StringUtils.isNotEmpty(assigneeValue)) {
                TaskHelper.changeTaskAssignee(task, assigneeValue);
                if (processEngineConfiguration.isLoggingSessionEnabled()) {
                    ObjectNode loggingNode = BpmnLoggingSessionUtil.fillBasicTaskLoggingData("Set task assignee value to " + assigneeValue, task, execution);
                    loggingNode.put("taskAssignee", assigneeValue);
                    LoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_USER_TASK_SET_ASSIGNEE, loggingNode, ScopeTypes.BPMN);
                }
            }

            //如果是会签节点, 在这里往operation表插入数据
            if(hasLoopCharacteristics()){
                String processDefinitionId = task.getProcessDefinitionId();
                String taskDefinitionKey = task.getTaskDefinitionKey();
                Process process = getProcessDefinition(processDefinitionId);
                String taskProperty = getAttributeValueByTaskDefinitionKey(process, task.getTaskDefinitionKey(), "userTask");
                UserTaskExPropertyDTO taskExPropertyDTO = JSONObject.parseObject(taskProperty.toString(), new TypeReference<UserTaskExPropertyDTO>() {
                });
                operateHqUserTask(taskExPropertyDTO,task);
            }



        }

        if (StringUtils.isNotEmpty(owner)) {
            Object ownerExpressionValue = expressionManager.createExpression(owner).getValue(execution);
            String ownerValue = null;
            if (ownerExpressionValue != null) {
                ownerValue = ownerExpressionValue.toString();
            }

            if (StringUtils.isNotEmpty(ownerValue)) {
                TaskHelper.changeTaskOwner(task, ownerValue);
                if (processEngineConfiguration.isLoggingSessionEnabled()) {
                    ObjectNode loggingNode = BpmnLoggingSessionUtil.fillBasicTaskLoggingData("Set task owner value to " + ownerValue, task, execution);
                    loggingNode.put("taskOwner", ownerValue);
                    LoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_USER_TASK_SET_OWNER, loggingNode, ScopeTypes.BPMN);
                }
            }
        }

        if (candidateGroups != null && !candidateGroups.isEmpty()) {
            List<IdentityLinkEntity> allIdentityLinkEntities = new ArrayList<>();
            for (String candidateGroup : candidateGroups) {
                Expression groupIdExpr = expressionManager.createExpression(candidateGroup);
                Object value = groupIdExpr.getValue(execution);
                if (value != null) {
                    Collection<String> candidates = extractCandidates(value);
                    List<IdentityLinkEntity> identityLinkEntities = processEngineConfiguration.getIdentityLinkServiceConfiguration()
                            .getIdentityLinkService().addCandidateGroups(task.getId(), candidates);

                    if (identityLinkEntities != null && !identityLinkEntities.isEmpty()) {
                        IdentityLinkUtil.handleTaskIdentityLinkAdditions(task, identityLinkEntities);
                        allIdentityLinkEntities.addAll(identityLinkEntities);
                    }
                }
            }

            if (!allIdentityLinkEntities.isEmpty()) {
                if (processEngineConfiguration.isLoggingSessionEnabled()) {
                    BpmnLoggingSessionUtil.addTaskIdentityLinkData(LoggingSessionConstants.TYPE_USER_TASK_SET_GROUP_IDENTITY_LINKS,
                            "Added " + allIdentityLinkEntities.size() + " candidate group identity links to task", false,
                            allIdentityLinkEntities, task, execution);
                }
            }
        }

        if (candidateUsers != null && !candidateUsers.isEmpty()) {
            List<IdentityLinkEntity> allIdentityLinkEntities = new ArrayList<>();
            for (String candidateUser : candidateUsers) {
                Expression userIdExpr = expressionManager.createExpression(candidateUser);
                Object value = userIdExpr.getValue(execution);
                if (value != null) {
                    Collection<String> candidates = extractCandidates(value);
                    List<IdentityLinkEntity> identityLinkEntities = processEngineConfiguration.getIdentityLinkServiceConfiguration()
                            .getIdentityLinkService().addCandidateUsers(task.getId(), candidates);

                    if (identityLinkEntities != null && !identityLinkEntities.isEmpty()) {
                        IdentityLinkUtil.handleTaskIdentityLinkAdditions(task, identityLinkEntities);
                        allIdentityLinkEntities.addAll(identityLinkEntities);
                    }
                }
            }

            if (!allIdentityLinkEntities.isEmpty()) {
                if (processEngineConfiguration.isLoggingSessionEnabled()) {
                    BpmnLoggingSessionUtil.addTaskIdentityLinkData(LoggingSessionConstants.TYPE_USER_TASK_SET_USER_IDENTITY_LINKS,
                            "Added " + allIdentityLinkEntities.size() + " candidate user identity links to task", true,
                            allIdentityLinkEntities, task, execution);
                }
            }
        }

        if (userTask.getCustomUserIdentityLinks() != null && !userTask.getCustomUserIdentityLinks().isEmpty()) {

            List<IdentityLinkEntity> customIdentityLinkEntities = new ArrayList<>();
            for (String customUserIdentityLinkType : userTask.getCustomUserIdentityLinks().keySet()) {
                for (String userIdentityLink : userTask.getCustomUserIdentityLinks().get(customUserIdentityLinkType)) {
                    Expression idExpression = expressionManager.createExpression(userIdentityLink);
                    Object value = idExpression.getValue(execution);

                    Collection<String> userIds = extractCandidates(value);
                    for (String userId : userIds) {
                        IdentityLinkEntity identityLinkEntity = processEngineConfiguration.getIdentityLinkServiceConfiguration()
                                .getIdentityLinkService().createTaskIdentityLink(task.getId(), userId, null, customUserIdentityLinkType);
                        IdentityLinkUtil.handleTaskIdentityLinkAddition(task, identityLinkEntity);
                        customIdentityLinkEntities.add(identityLinkEntity);
                    }
                }
            }

            if (!customIdentityLinkEntities.isEmpty()) {
                if (processEngineConfiguration.isLoggingSessionEnabled()) {
                    BpmnLoggingSessionUtil.addTaskIdentityLinkData(LoggingSessionConstants.TYPE_USER_TASK_SET_USER_IDENTITY_LINKS,
                            "Added " + customIdentityLinkEntities.size() + " custom user identity links to task", true,
                            customIdentityLinkEntities, task, execution);
                }
            }
        }

        if (userTask.getCustomGroupIdentityLinks() != null && !userTask.getCustomGroupIdentityLinks().isEmpty()) {

            List<IdentityLinkEntity> customIdentityLinkEntities = new ArrayList<>();
            for (String customGroupIdentityLinkType : userTask.getCustomGroupIdentityLinks().keySet()) {
                for (String groupIdentityLink : userTask.getCustomGroupIdentityLinks().get(customGroupIdentityLinkType)) {

                    Expression idExpression = expressionManager.createExpression(groupIdentityLink);
                    Object value = idExpression.getValue(execution);
                    Collection<String> groupIds = extractCandidates(value);
                    for (String groupId : groupIds) {
                        IdentityLinkEntity identityLinkEntity = processEngineConfiguration.getIdentityLinkServiceConfiguration()
                                .getIdentityLinkService().createTaskIdentityLink(
                                        task.getId(), null, groupId, customGroupIdentityLinkType);
                        IdentityLinkUtil.handleTaskIdentityLinkAddition(task, identityLinkEntity);
                        customIdentityLinkEntities.add(identityLinkEntity);
                    }
                }
            }

            if (!customIdentityLinkEntities.isEmpty()) {
                if (processEngineConfiguration.isLoggingSessionEnabled()) {
                    BpmnLoggingSessionUtil.addTaskIdentityLinkData(LoggingSessionConstants.TYPE_USER_TASK_SET_GROUP_IDENTITY_LINKS,
                            "Added " + customIdentityLinkEntities.size() + " custom group identity links to task", false,
                            customIdentityLinkEntities, task, execution);
                }
            }
        }

    }


    /**
     *
     * @param userTaskExPropertyDTO
     */
    private void operateHqUserTask(UserTaskExPropertyDTO userTaskExPropertyDTO, Task taskEntity){
        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(taskEntity.getAssignee());
        operator.setTaskNodeId(taskEntity.getId());
        operator.setTaskId(taskEntity.getProcessInstanceId());
        operator.setNodeCode(taskEntity.getTaskDefinitionKey());
        operator.setNodeName(taskEntity.getName());
        operator.setDescription(description);
        operator.setCreatorTime(new Date());
        operator.setCompletion(Integer.valueOf(userTaskBasicPropDTO.getProgress()));
        operator.setType(type);
        operator.setState(FlowNodeEnum.Process.getCode());
        //保存
        FlowTaskOperatorService flowTaskOperatorService = SpringContextHolder.getBean(FlowTaskOperatorService.class);
        flowTaskOperatorService.save(operator);
    }


    public void setMultiInstanceActivityBehavior(
            MultiInstanceActivityBehavior multiInstanceActivityBehavior) {
        // TODO Auto-generated method stub
        log.info("MultiInstance usertask  -- {}", multiInstanceActivityBehavior);
        super.setMultiInstanceActivityBehavior(multiInstanceActivityBehavior);
    }

    @Override
    public void execute(DelegateExecution execution)  {
        log.info("{}:{} begin execute", execution.getCurrentActivityId(),
                execution.getCurrentFlowElement().getName());
        super.execute(execution);
        log.info("{}:{} after execute", execution.getCurrentActivityId(),
                execution.getCurrentFlowElement().getName());
    }
}
