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

import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCancelDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.engine.job.AsyncCancelProcessInstanceHandler;
import cn.hutool.json.JSONUtil;
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.history.HistoricProcessInstance;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.job.service.JobService;
import org.flowable.job.service.impl.persistence.entity.JobEntity;

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

import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.ABORTED;
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;

public class CustomCancelProcessInstanceAsyncCmd implements Command<Void>, Serializable {

    private final BpmnProcessInstanceCancelDTO dto;

    public CustomCancelProcessInstanceAsyncCmd(BpmnProcessInstanceCancelDTO dto) {
        this.dto = dto;
    }

    @Override
    public Void execute(CommandContext commandContext) {
        String processInstanceId = dto.getProcessInstanceId(), tenantId = dto.getTenantId();
        BpmnTaskDelegateAssigner initiator = dto.getInitiator();
        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);
        }
        startAsync(processEngineConfiguration, instance);
        return null;
    }

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

        JobEntity job = jobService.createJob();
        // 这里的 executionId 可为 null
        job.setExecutionId(instance.getId());
        job.setProcessInstanceId(instance.getId());
        job.setProcessDefinitionId(instance.getProcessDefinitionId());
        job.setElementId(AsyncCancelProcessInstanceHandler.TYPE);
        job.setElementName(instance.getName());
        job.setJobHandlerType(AsyncCancelProcessInstanceHandler.TYPE);
        job.setTenantId(instance.getTenantId());

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

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