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

import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskTransferDTO;
import cn.axzo.workflow.core.engine.job.AsyncTransferUserTaskJobHandler;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.job.service.JobService;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
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 static cn.axzo.workflow.common.code.BpmnTaskRespCode.TRANSFER_TO_SELF;
import static cn.axzo.workflow.common.code.OtherRespCode.ASSIGNEE_NODE_ID_NOT_EXISTS;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getCategoryVersion;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;

/**
 * 异步转交任务的命令执行器
 */
public class CustomTransferUserTaskAsyncCmd extends AbstractCommand<Void> implements Serializable {

    private static final long serialVersionUID = -1847844435303542933L;

    private final BpmnTaskTransferDTO dto;

    public CustomTransferUserTaskAsyncCmd(BpmnTaskTransferDTO dto) {
        this.dto = dto;
    }

    @Override
    public String paramToJsonString() {
        return JSON.toJSONString(dto);
    }

    @Override
    public Void execute(CommandContext commandContext) {
        //不能转交给自己
        if (dto.getOriginAssigner().buildAssigneeId().equals(dto.getTargetAssigner().buildAssigneeId())) {
            throw new WorkflowEngineException(TRANSFER_TO_SELF);
        }
        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        HistoricTaskInstanceQuery taskQuery = processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
        HistoricTaskInstance historicTaskInstance = taskQuery.taskId(dto.getTaskId()).singleResult();

        TaskService taskService = processEngineConfiguration.getTaskService();
        Task task = taskService.createTaskQuery().taskId(dto.getTaskId()).singleResult();
        validTargetAssigneeNodeId(task.getProcessDefinitionId());

        validTask(historicTaskInstance, (TaskEntity) task, dto.getOriginAssigner(), null);
        startAsync(processEngineConfiguration, task);
        return null;
    }

    private void startAsync(ProcessEngineConfigurationImpl processEngineConfiguration, Task task) {
        JobService jobService = processEngineConfiguration.getJobServiceConfiguration().getJobService();

        JobEntity job = jobService.createJob();
        // 这里的 executionId 可为 null
        job.setExecutionId(task.getExecutionId());
        job.setProcessInstanceId(task.getProcessInstanceId());
        job.setProcessDefinitionId(task.getProcessDefinitionId());
        job.setElementId(task.getTaskDefinitionKey());
        job.setElementName(task.getName());
        job.setJobHandlerType(AsyncTransferUserTaskJobHandler.TYPE);
        job.setTenantId(task.getTenantId());

        // 携带自定义的数据
        job.setCustomValues(JSONUtil.toJsonStr(dto));

        // 创建异步任务并调度
        jobService.createAsyncJob(job, false);
        jobService.scheduleAsyncJob(job);
    }

    private void validTargetAssigneeNodeId(String processDefinitionId) {
        BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionId);
        Integer categoryVersion = getCategoryVersion(bpmnModel.getMainProcess()).orElse(0);
        if (categoryVersion > 0 && StringUtils.isBlank(dto.getTargetAssigner().getNodeId())) {
            throw new WorkflowEngineException(ASSIGNEE_NODE_ID_NOT_EXISTS, "审批人");
        }
    }

}
