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

import cn.axzo.workflow.common.enums.ApproverSpecifyEnum;
import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeProperty;
import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeReceiver;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceVO;
import cn.axzo.workflow.core.engine.listener.EngineExecutionStartListener;
import cn.axzo.workflow.core.engine.model.NoticeFlowElement;
import cn.axzo.workflow.core.service.converter.BpmnHistoricTaskInstanceConverter;
import com.alibaba.fastjson.JSON;
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.RuntimeService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.runtime.Execution;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE;
import static cn.axzo.workflow.common.constant.BpmnConstants.OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.removeDuplicateByPersonId;

/**
 * 消息通知,根据模型全局配置查找通知用户
 *
 * @author wangli
 * @since 2024/5/11 09:42
 */
public class CustomNoticeDestinationUserSelectorCmd implements Command<List<BpmnTaskDelegateAssigner>>, Serializable {
    private static final long serialVersionUID = 1L;
    private final EngineExecutionStartListener engineExecutionStartListener;
    private final BpmnHistoricTaskInstanceConverter historicTaskInstanceConverter;
    private final String serviceVersion;
    /**
     * 流程模型对应的工作台类型, 这个值在创建流程时,已经放入到了流程变量中
     */
    private final Integer workspaceType;
    private final BpmnNoticeProperty noticeProperty;
    private final String processInstanceId;
    private final BpmnTaskDelegateAssigner initiator;

    public CustomNoticeDestinationUserSelectorCmd(EngineExecutionStartListener engineExecutionStartListener, BpmnHistoricTaskInstanceConverter historicTaskInstanceConverter, String serviceVersion, Integer workspaceType, BpmnNoticeProperty noticeProperty, String processInstanceId, BpmnTaskDelegateAssigner initiator) {
        this.engineExecutionStartListener = engineExecutionStartListener;
        this.historicTaskInstanceConverter = historicTaskInstanceConverter;
        this.serviceVersion = serviceVersion;
        this.workspaceType = workspaceType;
        this.noticeProperty = noticeProperty;
        this.processInstanceId = processInstanceId;
        this.initiator = initiator;
    }

    @Override
    public List<BpmnTaskDelegateAssigner> execute(CommandContext commandContext) {
        if (!noticeProperty.getSendMessage()) {
            return Collections.emptyList();
        }
        ProcessEngineConfigurationImpl processEngineConfiguration =
                CommandContextUtil.getProcessEngineConfiguration(commandContext);
        List<BpmnTaskDelegateAssigner> assigners = new ArrayList<>();
        checkAndBuildInitiator(assigners);
        checkAndBuildHistories(assigners, processEngineConfiguration);
        checkAndBuildRoles(assigners, processEngineConfiguration);
        checkAndBuildPositions(assigners, processEngineConfiguration);
        checkAndBuildAssigners(assigners);
        return removeDuplicateByPersonId(assigners);
    }

    private void checkAndBuildAssigners(List<BpmnTaskDelegateAssigner> assigners) {
        if (Objects.isNull(noticeProperty.getAssigners()) || !noticeProperty.getAssigners().getSelected()) {
            return;
        }
        assigners.addAll(JSON.parseArray(noticeProperty.getAssigners().getViewJson(), BpmnTaskDelegateAssigner.class));
    }

    private void checkAndBuildPositions(List<BpmnTaskDelegateAssigner> assigners, ProcessEngineConfigurationImpl processEngineConfiguration) {
        if (Objects.isNull(noticeProperty.getPositions()) || !noticeProperty.getPositions().getSelected()) {
            return;
        }
        RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
        List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list();
        assigners.addAll(engineExecutionStartListener.approverSelect(ApproverSpecifyEnum.position.getType(),
                buildCustomFlowElement(ApproverSpecifyEnum.position, noticeProperty.getPositions()),
                (DelegateExecution) executions.get(0), false));
    }

    private void checkAndBuildRoles(List<BpmnTaskDelegateAssigner> assigners, ProcessEngineConfigurationImpl processEngineConfiguration) {
        if (Objects.isNull(noticeProperty.getRoles()) || !noticeProperty.getRoles().getSelected()) {
            return;
        }
        RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
        List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list();
        assigners.addAll(engineExecutionStartListener.approverSelect(ApproverSpecifyEnum.role.getType(),
                buildCustomFlowElement(ApproverSpecifyEnum.role, noticeProperty.getRoles()),
                (DelegateExecution) executions.get(0), false));
    }

    private void checkAndBuildHistories(List<BpmnTaskDelegateAssigner> assigners, ProcessEngineConfigurationImpl processEngineConfiguration) {
        if (Objects.isNull(noticeProperty.getHistories()) || !noticeProperty.getHistories().getSelected()) {
            return;
        }
        HistoryService historyService = processEngineConfiguration.getHistoryService();
        // 历史真正有同意和驳回动作的人
        List<HistoricTaskInstance> taskInstances = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                //                            .includeProcessVariables()
                .orderByHistoricTaskInstanceStartTime()
                .desc().list();
        List<BpmnHistoricTaskInstanceVO> vos =
                historicTaskInstanceConverter.toVosSkipSystemOperation(taskInstances,
                        serviceVersion);

        Map<String, HistoricVariableInstance> variableInstanceMap =
                // 不能使用框架提供的历史变量 API 查询,有 BUG
                historyService.createNativeHistoricVariableInstanceQuery()
                        .sql("select * from ACT_HI_VARINST t where t.proc_inst_id_= #{processInstanceId}")
                        .parameter("processInstanceId", processInstanceId)
                        .list().stream()
                        .collect(Collectors.toMap(HistoricVariableInstance::getVariableName,
                                Function.identity(), (s, t) -> s));
        vos.forEach(vo -> {
            HistoricVariableInstance assginerSnapshot =
                    variableInstanceMap.getOrDefault(INTERNAL_TASK_RELATION_ASSIGNEE_INFO + vo.getTaskId(),
                            null);
            if (Objects.isNull(assginerSnapshot)) {
                assginerSnapshot =
                        variableInstanceMap.getOrDefault(OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT + vo.getTaskId(), null);
            }
            if (Objects.nonNull(assginerSnapshot)) {
                BpmnTaskDelegateAssigner assigner = BpmnTaskDelegateAssigner.toObjectCompatible(assginerSnapshot.getValue());
                if (Objects.nonNull(assigner) && !Objects.equals(assigner.buildAssigneeId(), NO_ASSIGNEE)) {
                    assigners.add(assigner);
                }
            }
        });
    }

    /**
     * 判断通知对象是否勾选发起人,如勾选则将发起人加入发送目标对象集合中
     *
     * @param assigners
     */
    private void checkAndBuildInitiator(List<BpmnTaskDelegateAssigner> assigners) {
        if (Objects.isNull(noticeProperty.getInitiator()) || !noticeProperty.getInitiator().getSelected()) {
            return;
        }
        assigners.add(initiator);
    }

    private NoticeFlowElement buildCustomFlowElement(ApproverSpecifyEnum specifyEnum, BpmnNoticeReceiver receiver) {
        NoticeFlowElement noticeFlowElement = new NoticeFlowElement();
        noticeFlowElement.setId("noticeFlowElement");
        noticeFlowElement.setName("消息推送模拟流程元素");
        noticeFlowElement.setWorkspaceType(workspaceType);
        noticeFlowElement.setSpecifyEnum(specifyEnum);
        noticeFlowElement.setCustomValues(receiver.getViewJson());
        return noticeFlowElement;
    }


}
