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

import cn.axzo.framework.jackson.utility.JSON;
import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.deletage.BpmnTaskDelegate;
import cn.axzo.workflow.core.engine.event.MessagePushEventBuilder;
import cn.axzo.workflow.core.engine.event.MessagePushEventImpl;
import cn.axzo.workflow.core.engine.event.MessagePushEventType;
import cn.axzo.workflow.core.listener.BpmnMessagePushEventListener;
import cn.axzo.workflow.core.listener.BpmnTaskEventListener;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;

/**
 * 引擎用户任务事件监听, 用于 BPMN 文件中为节点设置指定类型监听
 *
 * @author wangli
 */
@Slf4j
@Component
public class EngineTaskEventListener implements TaskListener {

    @Resource
    ObjectProvider<List<BpmnTaskEventListener>> taskEventListeners;

    @Resource
    ObjectProvider<List<BpmnMessagePushEventListener>> messagePushEventListeners;

    @Resource
    @Lazy
    private BpmnTaskDelegate taskDelegate;
    @Resource
    private RuntimeService runtimeService;

    @Override
    public void notify(DelegateTask delegateTask) {
        log.info("Task.notify {} taskDelegate:{}", delegateTask.getId(), taskDelegate);

        getOrderedListeners().forEach(i -> {
            if (Objects.isNull(delegateTask.getEventName())) {
                // FIXME 地址不知道为什么从 1.2.1 版本出现了NPE, 通过堆栈未找到根因. 有空再跟进
                return;
            }
            switch (delegateTask.getEventName()) {
                case EVENTNAME_CREATE:
                    i.onCreated(delegateTask);
                    break;
                case EVENTNAME_ASSIGNMENT:
                    i.onAssigned(delegateTask);

                    pushPending(delegateTask);
                    break;
                case EVENTNAME_COMPLETE:
                    //审批完成
                    i.onCompleted(delegateTask);
                    break;
                case EVENTNAME_DELETE:
                    // 审批删除
                    i.onDeleted(delegateTask);
                    break;
                default:
            }
        });

    }

    private void pushPending(DelegateTask delegateTask) {
        ProcessInstance processInstance =
                runtimeService.createProcessInstanceQuery().processInstanceId(delegateTask.getProcessInstanceId()).includeProcessVariables().singleResult();
        List<BpmnTaskDelegateAssigner> assigners =
                (List<BpmnTaskDelegateAssigner>) processInstance.getProcessVariables()
                        .getOrDefault(INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + delegateTask.getTaskDefinitionKey(), Collections.emptyList());
        if (CollectionUtils.isEmpty(assigners)) {
            return;
        }

        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
        FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
        Optional<BpmnNoticeConf> noticeConfig =
                BpmnMetaParserHelper.getNoticeConfig(ProcessDefinitionUtil.getProcess(processInstance.getProcessDefinitionId()));
        assigners.stream().filter(i -> Objects.equals(delegateTask.getAssignee(), i.buildAssigneeId())).findAny()
                .ifPresent(i -> {
                    MessagePushEventImpl event = MessagePushEventBuilder.createEvent(MessagePushEventType.NOTICE, i,
                            noticeConfig.orElse(null));
                    eventDispatcher.dispatchEvent(event, processEngineConfiguration.getEngineCfgKey());
                });
    }

    private List<BpmnTaskEventListener> getOrderedListeners() {
        List<BpmnTaskEventListener> orderListeners = new ArrayList<>();
        taskEventListeners.ifAvailable(orderListeners::addAll);
        if (log.isDebugEnabled()) {
            log.debug("Order Lists: {}", JSON.toJSONString(orderListeners));
        }
        return orderListeners;
    }

    private List<BpmnMessagePushEventListener> getOrderedMessagePushListeners() {
        List<BpmnMessagePushEventListener> orderListeners = new ArrayList<>();
        messagePushEventListeners.ifAvailable(orderListeners::addAll);
        if (log.isDebugEnabled()) {
            log.debug("Order Lists: {}", JSON.toJSONString(orderListeners));
        }
        return orderListeners;
    }
}
