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

import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.dto.SimpleTaskDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCompleteDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper;
import cn.axzo.workflow.core.engine.tx.listener.RobotTaskTransactionListener;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.axzo.workflow.core.service.ExtAxProcessLogService;
import com.alibaba.fastjson.JSON;
import org.flowable.common.engine.impl.cfg.TransactionState;
import org.flowable.common.engine.impl.identity.Authentication;
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.context.Context;
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 org.springframework.util.StringUtils;

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

import static cn.axzo.workflow.common.code.BpmnTaskRespCode.DUMMY_TASK_NOT_EXISTS;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
import static cn.axzo.workflow.common.constant.BpmnConstants.HIDDEN_ASSIGNEE_ID;
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_ROBOT;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.batchAddAttachment;

/**
 * 自定义的完成虚拟任务命令实现
 *
 * @author wangli
 * @since 2023/12/27 11:24
 */
public class CustomCompleteDummyTaskCmd extends AbstractCommand<Void> implements Serializable {
    private final String processInstanceId;
    private final String taskId;
    private String flowNodeName;
    private String operationDesc;
    private final List<AttachmentDTO> attachmentList;
    private final BpmnTaskDelegateAssigner operator;
    private final ExtAxHiTaskInstService extAxHiTaskInstService;
    private final ExtAxProcessLogService extAxProcessLogService;

    public CustomCompleteDummyTaskCmd(BpmnRobotTaskCompleteDTO dto,
                                      ExtAxHiTaskInstService extAxHiTaskInstService,
                                      ExtAxProcessLogService extAxProcessLogService) {
        this.processInstanceId = dto.getProcessInstanceId();
        this.taskId = dto.getTaskId();
        if (Objects.nonNull(dto.getRobotNode())) {
            this.flowNodeName = dto.getRobotNode().getFlowNodeName();
            this.operationDesc = dto.getRobotNode().getOperationDesc();
        }
        this.attachmentList = dto.getAttachmentList();
        this.operator = dto.getApprover();
        this.extAxHiTaskInstService = extAxHiTaskInstService;
        this.extAxProcessLogService = extAxProcessLogService;
    }

    @Override
    public String paramToJsonString() {
        Map<String, Object> params = new HashMap<>();
        params.put("processInstanceId", processInstanceId);
        params.put("taskId", taskId);
        params.put("flowNodeName", flowNodeName);
        params.put("operationDesc", operationDesc);
        return JSON.toJSONString(params);
    }

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

        TaskService taskService = processEngineConfiguration.getTaskService();
        TaskEntity task = (TaskEntity) taskService.createTaskQuery().processInstanceId(processInstanceId)
                .taskId(taskId).singleResult();
        if (Objects.isNull(task)) {
            throw new WorkflowEngineException(DUMMY_TASK_NOT_EXISTS, processInstanceId, taskId);
        }
        if (StringUtils.hasText(flowNodeName)) {
            task.setName(flowNodeName);
            taskService.saveTask(task);
        }

        if (StringUtils.hasText(operationDesc)) {
            Authentication.setAuthenticatedUserId("system");
            CustomTaskHelper.addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, operationDesc);
            Authentication.setAuthenticatedUserId(null);
        }

        BpmnTaskDelegateAssigner assignee = BpmnTaskDelegateAssigner.buildDummyAssigner("system",
                TASK_ASSIGNEE_SKIP_FLAT, "系统");
        batchAddAttachment(commandContext, task.getProcessInstanceId(), task, attachmentList, assignee);

        task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + taskId, APPROVED.getStatus());
        taskService.complete(task.getId());

        continueProcessingTask(commandContext);

        CustomTaskHelper.createExtTaskInst(extAxHiTaskInstService, processInstanceId, task.getId(),
                NODE_ROBOT.getType(), assignee, BpmnProcessInstanceResultEnum.APPROVED.getStatus());
        return null;
    }

    private void continueProcessingTask(CommandContext commandContext) {
        ProcessEngineConfigurationImpl processEngineConfiguration =
                CommandContextUtil.getProcessEngineConfiguration(commandContext);
        TaskService taskService = processEngineConfiguration.getTaskService();
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstanceId).active().list();
        if (CollectionUtils.isEmpty(taskList)) {
            return;
        }

        List<SimpleTaskDTO> restoreTasks = new ArrayList<>();
        taskList.stream().filter(i -> !Objects.equals(i.getTaskDefinitionKey(), NODE_ROBOT.getType()))
                .filter(i -> Objects.equals(i.getAssignee(), HIDDEN_ASSIGNEE_ID))
                .forEach(i -> {
                    taskService.setAssignee(i.getId(), i.getOwner());
                    taskService.setOwner(i.getId(), null);

                    restoreTasks.add(new SimpleTaskDTO(i.getProcessInstanceId(), i.getId()));
                });

        Context.getTransactionContext().addTransactionListener(TransactionState.COMMITTED, new RobotTaskTransactionListener(restoreTasks, extAxProcessLogService));
    }

}
