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.cmd.helper.CustomTaskHelper;
import org.flowable.common.engine.impl.interceptor.Command;
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.service.impl.persistence.entity.TaskEntity;
import org.springframework.util.CollectionUtils;

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

import static cn.axzo.workflow.common.constant.BpmnConstants.HIDDEN_ASSIGNEE_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE;
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.core.common.code.BpmnTaskRespCode.ACTIVITY_BIZ_SET_ASSIGNEE_ERROR;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ACTIVITY_CANT_SET_ASSIGNEE;

/**
 * 自定的业务指定审批人命令实现
 *
 * @author wangli
 * @since 2023/12/22 13:51
 */
public class CustomBizSpecifyAssigneeToTaskCmd implements Command<Boolean>, Serializable {

    private final String executionId;
    private final List<BpmnTaskDelegateAssigner> addedAssigners;

    public CustomBizSpecifyAssigneeToTaskCmd(String executionId, List<BpmnTaskDelegateAssigner> addedAssigners) {
        this.executionId = executionId;
        this.addedAssigners = addedAssigners;
    }

    @Override
    public Boolean execute(CommandContext commandContext) {
        ProcessEngineConfigurationImpl processEngineConfiguration =
                CommandContextUtil.getProcessEngineConfiguration(commandContext);
        TaskService taskService = processEngineConfiguration.getTaskService();

        Task task = taskService.createTaskQuery().executionId(executionId)
                .taskAssignee(NO_ASSIGNEE)
                .singleResult();

        validTask(task);

        changeAssigneeSnapshot(commandContext, task);

        addAssignee(commandContext, taskService, task);

        return true;
    }

    private void changeAssigneeSnapshot(CommandContext commandContext, Task task) {
        ProcessEngineConfigurationImpl processEngineConfiguration =
                CommandContextUtil.getProcessEngineConfiguration(commandContext);
        RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();

        List<BpmnTaskDelegateAssigner> originAssingeeList = runtimeService.getVariable(task.getProcessInstanceId(),
                INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(), List.class);

        for (BpmnTaskDelegateAssigner assigner : originAssingeeList) {
            if (Objects.equals(assigner.buildAssigneeId(), NO_ASSIGNEE)) {
                originAssingeeList.remove(assigner);
                break;
            }
        }
        originAssingeeList.addAll(addedAssigners);
        runtimeService.setVariable(task.getProcessInstanceId(),
                INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(),
                originAssingeeList);
    }

    private void addAssignee(CommandContext commandContext, TaskService taskService, Task task) {
        if (CollectionUtils.isEmpty(addedAssigners)) {
            return;
        }

        addedAssigners.forEach(i -> {
            CustomTaskHelper.addMultiTask(commandContext, (TaskEntity) task, i);
        });

        taskService.setAssignee(task.getId(), HIDDEN_ASSIGNEE_ID);
        ((TaskEntity) task).setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), DELETED.getStatus());
        CustomTaskHelper.deleteMultiTask(commandContext, (TaskEntity) task);
    }

    private void validTask(Task task) {
        if (Objects.isNull(task)) {
            throw new WorkflowEngineException(ACTIVITY_BIZ_SET_ASSIGNEE_ERROR, executionId);
        }
        if (!Objects.equals(task.getAssignee(), NO_ASSIGNEE)) {
            throw new WorkflowEngineException(ACTIVITY_CANT_SET_ASSIGNEE);
        }
    }

}
