package cn.axzo.workflow.core.engine.cmd;

import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.engine.operation.DeleteProcessInstanceOperation;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.task.api.Task;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_DELETE_PROCESS_FLAG;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_TENANT_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_NAME;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_DELETE_REASON;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_TYPE_CANCEL;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_CANCEL;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.ABORTED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.CANCELLED;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANT_CANCEL;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_NOT_EXISTS;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.createVirtualTask;

/**
 * 自定义的撤回流程实例的命令实现
 *
 * @author wangli
 * @since 2024/1/2 18:19
 */
public class CustomCancelProcessInstanceCmd implements Command<Void>, Serializable {
    private final String processInstanceId;
    private final String tenantId;
    private final String reason;
    private final BpmnTaskDelegateAssigner initiator;
    private final ExtAxHiTaskInstService extAxHiTaskInstService;

    public CustomCancelProcessInstanceCmd(String processInstanceId, String tenantId, String reason,
                                          BpmnTaskDelegateAssigner initiator,
                                          ExtAxHiTaskInstService extAxHiTaskInstService) {
        this.processInstanceId = processInstanceId;
        this.tenantId = tenantId;
        this.reason = reason;
        this.initiator = initiator;
        this.extAxHiTaskInstService = extAxHiTaskInstService;
    }

    @Override
    public Void execute(CommandContext commandContext) {
        ProcessEngineConfigurationImpl processEngineConfiguration =
                CommandContextUtil.getProcessEngineConfiguration(commandContext);

        HistoryService historyService = processEngineConfiguration.getHistoryService();
        HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .processInstanceTenantId(tenantId)
                .singleResult();
        if (Objects.isNull(instance)) {
            throw new WorkflowEngineException(PROCESS_INSTANCE_NOT_EXISTS);
        }

        if (Objects.nonNull(instance.getEndTime())) {
            throw new WorkflowEngineException(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
        }

        if (Objects.isNull(initiator) || !initiator.comparePersonIdToOther(instance.getStartUserId())) {
            throw new WorkflowEngineException(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
        }

        if (Objects.equals(instance.getBusinessStatus(), ABORTED.getStatus())) {
            throw new WorkflowEngineException(PROCESS_INSTANCE_CANT_CANCEL);
        }

        Map<String, Object> variables = new HashMap<>();
        variables.put(INTERNAL_END_TENANT_ID, tenantId);
        variables.put(INTERNAL_END_USER_ID, initiator.buildAssigneeId());
        variables.put(INTERNAL_END_USER_NAME, initiator.getAssignerName());
        variables.put(INTERNAL_DELETE_PROCESS_FLAG, INTERNAL_PROCESS_TYPE_CANCEL);
        variables.put(INTERNAL_PROCESS_DELETE_REASON, reason);
        RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
        runtimeService.setVariables(instance.getId(), variables);

        CommandContextUtil.getAgenda(commandContext).planOperation(new DeleteProcessInstanceOperation(commandContext,
                processInstanceId, extAxHiTaskInstService));

        // 添加自定义的节点,用于展示最后的操作
        Task task = createVirtualTask(commandContext, extAxHiTaskInstService, processInstanceId,
                "发起人撤回", NODE_CANCEL.getType(), reason, initiator, CANCELLED.getStatus());
        addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, "已撤回");
        return null;
    }
}
