package cn.fateverse.workflow.process.listener;

import cn.fateverse.common.core.exception.CustomException;
import cn.fateverse.workflow.constant.ProcessConstant;
import cn.fateverse.workflow.entity.HistoricalOperation;
import cn.fateverse.workflow.entity.bpmn.ProcessNode;
import cn.fateverse.workflow.entity.bpmn.Properties;
import cn.fateverse.workflow.entity.bpmn.UserInfo;
import cn.fateverse.workflow.enums.OperationStateEnums;
import cn.fateverse.workflow.enums.OperationEnums;
import cn.fateverse.workflow.mapper.HistoricalOperationMapper;
import cn.fateverse.workflow.mapper.ProcessMapper;
import cn.fateverse.workflow.process.ProcessServiceUtils;
import cn.fateverse.workflow.process.TriggerService;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * ServiceTask服务监听类
 *
 * @author Clay
 * @date 2023/1/2
 */
@Slf4j
@Component
public class ServiceListener implements JavaDelegate {

    private final ProcessMapper processMapper;

    private final TriggerService triggerService;

    private final RepositoryService repositoryService;

    private final HistoricalOperationMapper operationMapper;


    public ServiceListener(ProcessMapper processMapper,
                           RepositoryService repositoryService,
                           HistoricalOperationMapper operationMapper,
                           TriggerService triggerService) {
        this.processMapper = processMapper;
        this.repositoryService = repositoryService;
        this.operationMapper = operationMapper;
        this.triggerService = triggerService;
    }

    @Override
    public void execute(DelegateExecution execution) {
        //获取到流程数据,并找到当前节点的数据信息
        String value = ProcessServiceUtils.getBpmnValue(repositoryService, execution.getProcessDefinitionId(), ProcessConstant.BPMN_FORM_PROCESS);
        Optional<ProcessNode> processNodeOptional = JSON.parseArray(value, ProcessNode.class).stream().filter(node -> node.getId().equals(execution.getCurrentActivityId())).findFirst();
        if (processNodeOptional.isEmpty()) {
            throw new CustomException("数据异常!");
        }
        ProcessNode processNode = processNodeOptional.get();
        switch (processNode.getType()) {
            case CC:
                sendCC(execution, processNode);
                break;
            case TRIGGER:
                triggerEvent(execution, processNode);
                break;
            default:
                throw new CustomException("数据异常!");
        }
    }

    /**
     * 处理触发事件
     *
     * @param execution   执行器
     * @param processNode 流程节点信息
     */
    private void triggerEvent(DelegateExecution execution, ProcessNode processNode) {
        HistoricalOperation operation = HistoricalOperation.toHistoricalOperation(execution, new ArrayList<>());
        Properties props = processNode.getProps();
        operation.setOperation(props.getType().equals("WEBHOOK") ? OperationEnums.TRIGGER_WEBHOOK : OperationEnums.TRIGGER_EMAIL);
        operation.setNodeId(processNode.getId());
        operation.setOperationName(processNode.getName());
        operation.setState(OperationStateEnums.RUNNING);
        switch (props.getType()) {
            case "WEBHOOK":
                //当使用脚本的时候则表示需要发起同步请求,此时的请求会影响整个流程
                if (props.getHttp().getHandlerByScript()) {
                    triggerService.sendHttpRequest(processNode, operation,execution);
                    saveOperation(operation);
                } else {
                    triggerService.asyncSendHttpRequest(processNode, operation,execution);
                    saveOperation(operation);
                }
                break;
            case "EMAIL":
                triggerService.asyncSendEmail(processNode, operation);
                saveOperation(operation);
                break;
            default:
                break;
        }
    }

    private void saveOperation(HistoricalOperation operation) {
        try {
            operationMapper.insert(operation);
        } catch (Exception e) {
            log.info("异步事务已经处理完成!");
        }
    }

    /**
     * 发起抄送
     *
     * @param execution   执行器信息
     * @param processNode 流程节点信息
     */
    private void sendCC(DelegateExecution execution, ProcessNode processNode) {
        Properties props = processNode.getProps();
        List<UserInfo> assignedUser = props.getAssignedUser();
        if (props.getShouldAdd()) {
            Map<String, Object> variables = execution.getVariables();
            JSONObject optionalUser = (JSONObject) variables.get(ProcessConstant.OPTIONAL_USER);
            if (null != optionalUser) {
                List<UserInfo> userInfos = optionalUser.getList(execution.getCurrentActivityId(), UserInfo.class);
                assignedUser.addAll(userInfos);
            }
        }
        //对审批用户进行去重操作
        assignedUser = assignedUser.stream().collect(Collectors.collectingAndThen(
                Collectors.toCollection(() -> new TreeSet<>(
                        Comparator.comparing(UserInfo::getId))), ArrayList::new
        ));
        List<TaskEntityImpl> taskList = new ArrayList<>(assignedUser.size());
        assignedUser.forEach(userInfo -> {
            String taskId = UUID.randomUUID().toString();
            TaskEntityImpl task = new TaskEntityImpl();
            task.setId(taskId);
            task.setProcessDefinitionId(execution.getProcessDefinitionId());
            task.setTaskDefinitionKey("cc-" + taskId);
            task.setProcessInstanceId(execution.getProcessInstanceId());
            task.setExecutionId(execution.getId());
            task.setName(processNode.getName());
            task.setAssignee(userInfo.getId());
            task.setPriority(50);
            taskList.add(task);
            userInfo.setState(OperationStateEnums.AGREE);
        });
        processMapper.batchInsertUserTaskInHistorical(taskList);
        HistoricalOperation operation = HistoricalOperation.toHistoricalOperation(execution, assignedUser);
        operation.setOperation(OperationEnums.CC);
        operation.setTaskId(UUID.randomUUID().toString());
        operation.setOperationName(processNode.getName());
        operationMapper.insert(operation);
    }


}
