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

import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCommentExtDTO;
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 cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.hutool.json.JSONUtil;
import org.flowable.common.engine.impl.cfg.IdGenerator;
import org.flowable.common.engine.impl.identity.Authentication;
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.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.task.service.impl.persistence.entity.HistoricTaskInstanceEntity;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.variable.service.HistoricVariableService;
import org.flowable.variable.service.impl.persistence.entity.HistoricVariableInstanceEntity;
import org.flowable.variable.service.impl.types.StringType;
import org.springframework.util.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_COMMENT_EXT;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_COMMENT;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.COMMENTED;
import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.TASK_CANT_COMMENT_INSTANCE_NOT_EXISTS;
import static org.flowable.task.api.Task.DEFAULT_PRIORITY;

/**
 * 自定义评论功能的命令实现
 *
 * @author wangli
 * @since 2023/12/26 16:14
 */
public class CustomCommentTaskCmd implements Command<Void>, Serializable {
    private final String processInstanceId;
    private final BpmnTaskDelegateAssigner operator;
    private final String comment;
    private final BpmnTaskCommentExtDTO commentExt;
    private final List<AttachmentDTO> attachmentList;
    private final ExtAxHiTaskInstService extAxHiTaskInstService;

    public CustomCommentTaskCmd(String processInstanceId, BpmnTaskDelegateAssigner operator, String comment,
                                BpmnTaskCommentExtDTO commentExt, List<AttachmentDTO> attachmentList,
                                ExtAxHiTaskInstService extAxHiTaskInstService) {
        this.processInstanceId = processInstanceId;
        this.operator = operator;
        this.comment = comment;
        this.commentExt = commentExt;
        this.attachmentList = attachmentList;
        this.extAxHiTaskInstService = extAxHiTaskInstService;
    }

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

        HistoricProcessInstance processInstance =
                historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (Objects.isNull(processInstance)) {
            throw new WorkflowEngineException(TASK_CANT_COMMENT_INSTANCE_NOT_EXISTS, processInstanceId);
        }
        TaskService taskService = processEngineConfiguration.getTaskService();
        IdGenerator idGenerator = processEngineConfiguration.getIdGenerator();

        TaskEntity task = (TaskEntity) taskService.newTask(idGenerator.getNextId());
        task.setCategory(processInstance.getProcessDefinitionKey().split(":")[0]);
        task.setDescription(processInstance.getDescription());
        task.setTenantId(processInstance.getTenantId());
        // 已完成的审批,不能在此时设置审批人
        // task.setAssignee(operator.buildAssigneeId());
        task.setName("评论");
        task.setProcessDefinitionId(processInstance.getProcessDefinitionId());
        task.setProcessInstanceId(processInstance.getId());
        task.setTaskDefinitionKey(NODE_COMMENT.getType());
        task.setPriority(DEFAULT_PRIORITY);
        task.setCreateTime(new Date());
        // 创建临时节点
        taskService.saveTask(task);
        // 处理该评论节点的评论人
        buildAndInsertHistoryVariable(task, processInstance, processEngineConfiguration);

        CommandContextUtil.getEntityCache().findInCache(HistoricTaskInstanceEntity.class).stream()
                .filter(i -> Objects.equals(i.getId(), task.getId())).findAny()
                .ifPresent(i -> i.setAssignee(operator.buildAssigneeId()));
        // 完成临时节点
        taskService.complete(task.getId());

        // 新增评论
        Authentication.setAuthenticatedUserId(operator.buildAssigneeId());
        if (StringUtils.hasText(comment)) {
            CustomTaskHelper.addComment(commandContext, task, COMMENT_TYPE_ADVICE, comment);
        }
        if (Objects.nonNull(commentExt)) {
            CustomTaskHelper.addComment(commandContext, task, COMMENT_TYPE_COMMENT_EXT, JSONUtil.toJsonStr(commentExt));
        }
        Authentication.setAuthenticatedUserId(null);

        // 处理附件
        CustomTaskHelper.batchAddAttachment(commandContext, processInstanceId, task.getId(), attachmentList, operator);

        CustomTaskHelper.createExtTaskInst(extAxHiTaskInstService, processInstanceId,
                task.getId(), task.getTaskDefinitionKey(), operator, COMMENTED.getStatus());
        return null;
    }

    private void buildAndInsertHistoryVariable(TaskEntity task,
                                               HistoricProcessInstance processInstance,
                                               ProcessEngineConfigurationImpl processEngineConfiguration) {
        HistoricVariableService historicVariableService =
                processEngineConfiguration.getVariableServiceConfiguration().getHistoricVariableService();
        HistoricVariableInstanceEntity historicVariableInstance =
                historicVariableService.createHistoricVariableInstance();
        historicVariableInstance.setTaskId(task.getId());
        historicVariableInstance.setExecutionId(task.getExecutionId());
        historicVariableInstance.setProcessInstanceId(processInstance.getId());
        historicVariableInstance.setName(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + task.getId());
        StringType variableType = new StringType(processEngineConfiguration.getMaxLengthString());
        historicVariableInstance.setVariableType(variableType);
        historicVariableInstance.setTextValue(operator.toJson());
        historicVariableInstance.setCreateTime(new Date());
        historicVariableInstance.setLastUpdatedTime(new Date());
        historicVariableService.insertHistoricVariableInstance(historicVariableInstance);
    }

    // 将对象转换为字节数组
    private static byte[] objectToByteArray(Serializable object) {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(bos)) {

            oos.writeObject(object);
            return bos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}
