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

import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO;
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.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;

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.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_REJECT;
import static cn.axzo.workflow.common.constant.BpmnConstants.SKIP_MQ;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.DELETED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.REJECTED;
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;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;

/**
 * 自定义驳回任务的命令实现
 *
 * @author wangli
 * @since 2024/1/4 13:36
 */
public class CustomRejectionTaskCmd extends AbstractCommand<Void> implements Serializable {

    private final String taskId;
    private final String advice;
    private String operationDesc;
    private final List<AttachmentDTO> attachmentList;
    private final BpmnTaskDelegateAssigner approver;
    private final ExtAxHiTaskInstService extAxHiTaskInstService;
    /**
     * 可为 null， 用于底层对当前操作的节点进行一些额外的过滤
     */
    private final List<BpmnFlowNodeType> nodeTypes;

    public CustomRejectionTaskCmd(BpmnTaskAuditDTO dto, ExtAxHiTaskInstService extAxHiTaskInstService) {
        this(dto, extAxHiTaskInstService, null);
        if (Objects.nonNull(dto.getOperationDesc())) {
            this.operationDesc = dto.getOperationDesc();
        }
    }

    public CustomRejectionTaskCmd(BpmnTaskAuditDTO dto, ExtAxHiTaskInstService extAxHiTaskInstService, String operationDesc) {
        this.taskId = dto.getTaskId();
        this.advice = dto.getAdvice();
        if (Objects.nonNull(operationDesc)) {
            this.operationDesc = operationDesc;
        } else {
            this.operationDesc = "已驳回";
        }
        this.attachmentList = dto.getAttachmentList();
        this.approver = dto.getApprover();
        this.extAxHiTaskInstService = extAxHiTaskInstService;
        this.nodeTypes = dto.getNodeTypes();
    }

    @Override
    public String paramToJsonString() {
        Map<String, Object> params = new HashMap<>();
        params.put("taskId", taskId);
        params.put("advice", advice);
        params.put("attachmentList", attachmentList);
        params.put("approver", approver);
        params.put("nodeTypes", nodeTypes);
        return JSON.toJSONString(params);
    }

    @Override
    public Void execute(CommandContext commandContext) {
        ProcessEngineConfigurationImpl processEngineConfiguration =
                CommandContextUtil.getProcessEngineConfiguration(commandContext);
        HistoricTaskInstanceQuery taskQuery =
                processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
        HistoricTaskInstance historicTaskInstance = taskQuery.taskId(taskId).singleResult();

        TaskService taskService = processEngineConfiguration.getTaskService();
        TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();

        validTask(historicTaskInstance, task, approver, nodeTypes);

        task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), DELETED.getStatus());
        TaskEntity virtualTask = createVirtualTask(commandContext, extAxHiTaskInstService, task.getProcessInstanceId(), task.getName(),
                task.getTaskDefinitionKey(), advice,
                Objects.equals(operationDesc, "自动驳回") ? null : approver, REJECTED.getStatus(),
                new AddComment(operationDesc));
        virtualTask.setTransientVariable(SKIP_MQ, true);

        batchAddAttachment(commandContext, task.getProcessInstanceId(), virtualTask, attachmentList, approver);
        completeVirtualTask(commandContext, virtualTask);

        RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
        finishProcessInstance(commandContext, runtimeService, task, advice);
        return null;
    }

    private void finishProcessInstance(CommandContext commandContext, RuntimeService runtimeService, Task task,
                                       String reason) {
        Map<String, Object> variables = new HashMap<>();
//        variables.put(INTERNAL_END_TENANT_ID, approver.getTenantId());
//        variables.put(INTERNAL_END_USER_NAME, approver.getAssignerName());
//        variables.put(INTERNAL_END_USER_ID, approver.buildAssigneeId());
        variables.put(INTERNAL_DELETE_PROCESS_FLAG, INTERNAL_PROCESS_TYPE_REJECT);
        variables.put(INTERNAL_PROCESS_DELETE_REASON, reason);
        runtimeService.setVariables(task.getProcessInstanceId(), variables);
        if (Objects.nonNull(approver)) {
            runtimeService.setVariable(task.getProcessInstanceId(), CLOSE_PROCESS_ASSIGNER, approver);
        }
        CommandContextUtil.getAgenda(commandContext)
                .planOperation(new DeleteProcessInstanceOperation(commandContext, task.getProcessInstanceId(),
                        extAxHiTaskInstService, REJECTED));
    }

}
