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

import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.engine.model.AddComment;
import cn.axzo.workflow.core.engine.operation.DeleteProcessInstanceOperation;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import com.alibaba.fastjson.JSON;
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.history.HistoricProcessInstanceQuery;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.util.StringUtils;

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

import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS;
import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANT_ABORT;
import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_NOT_EXISTS;
import static cn.axzo.workflow.common.constant.BpmnConstants.CLOSE_PROCESS_ASSIGNER;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_DELETE_PROCESS_FLAG;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_DELETE_REASON;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_TYPE_ABORT;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_ASSIGNEE_SKIP_FLAT;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_ABORT;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.ABORTED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.CANCELLED;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.batchAddAttachment;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.completeVirtualTask;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.createVirtualTask;

/**
 * 自定义的中止流程实例的命令实现
 *
 * @author wangli
 * @since 2024/1/2 18:19
 */
public class CustomAbortProcessInstanceCmd extends AbstractCommand<Void> implements Serializable {
    private static final long serialVersionUID = -4141519915611160506L;
    private final String processInstanceId;
    private final String tenantId;
    private final String reason;
    private final List<AttachmentDTO> attachmentList;
    private final String nodeName;
    private final BpmnTaskDelegateAssigner assigner;
    private final String signatureUrl;
    private final ExtAxHiTaskInstService extAxHiTaskInstService;
    private final String advice;

    public CustomAbortProcessInstanceCmd(BpmnProcessInstanceAbortDTO dto, ExtAxHiTaskInstService extAxHiTaskInstService) {
        this.processInstanceId = dto.getProcessInstanceId();
        this.tenantId = dto.getTenantId();
        this.reason = dto.getReason();
        this.attachmentList = dto.getAttachmentList();
        this.nodeName = StringUtils.hasText(dto.getNodeName()) ? dto.getNodeName() : "系统中止";
        this.assigner = Objects.nonNull(dto.getAssigner()) ? dto.getAssigner() :
                BpmnTaskDelegateAssigner.buildDummyAssigner("system", TASK_ASSIGNEE_SKIP_FLAT, "");
        this.signatureUrl = dto.getSignatureUrl();
        this.extAxHiTaskInstService = extAxHiTaskInstService;
        this.advice = dto.getAdvice();
    }

    @Override
    public String paramToJsonString() {
        Map<String, Object> params = new HashMap<>();
        params.put("processInstanceId", processInstanceId);
        params.put("tenantId", tenantId);
        params.put("reason", reason);
        params.put("attachmentList", JSON.toJSONString(attachmentList));
        params.put("nodeName", nodeName);
        params.put("assigner", assigner);
        params.put("signatureUrl", signatureUrl);
        return JSON.toJSONString(params);
    }

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

        HistoryService historyService = processEngineConfiguration.getHistoryService();
        HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId);
        if (StringUtils.hasText(tenantId)) {
            query.processInstanceTenantId(tenantId);
        }
        HistoricProcessInstance instance = query.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.equals(instance.getBusinessStatus(), CANCELLED.getStatus())) {
            throw new WorkflowEngineException(PROCESS_INSTANCE_CANT_ABORT);
        }

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

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

        // 添加自定义的节点,用于展示最后的操作
        TaskEntity task = createVirtualTask(commandContext, extAxHiTaskInstService, processInstanceId,
                nodeName, NODE_ABORT.getType(), advice, assigner, ABORTED.getStatus(), new AddComment(reason));
        runtimeService.setVariable(task.getProcessInstanceId(), TASK_COMPLETE_OPERATION_TYPE + task.getId(), ABORTED);

        batchAddAttachment(commandContext, task.getProcessInstanceId(), task, attachmentList, assigner);
        completeVirtualTask(commandContext, task);

        if (Objects.nonNull(assigner)) {
            runtimeService.setVariable(processInstanceId, CLOSE_PROCESS_ASSIGNER, assigner);
        }
        return null;
    }

}
