package cn.axzo.workflow.core.service.support.forecast;

import cn.axzo.workflow.core.service.support.forecast.impl.SequenceFlowForecasting;
import cn.axzo.workflow.core.service.support.spring.FlowableConditionEvaluatorAware;
import org.flowable.bpmn.model.FlowElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static cn.axzo.workflow.core.service.support.FlowNodeForecastService.FORECAST_MAP;

/**
 * 抽象的流程预测
 *
 * @author wangli
 * @since 2023/10/10 10:55
 */
public abstract class AbstractForecast<T extends FlowElement> implements Forecast<T>, FlowableConditionEvaluatorAware {

    private FlowableConditionEvaluator conditionEvaluator;
    private static final Logger log = LoggerFactory.getLogger(AbstractForecast.class);

    @Override
    public final List<FlowElement> nextFlowElement(FlowElement sourceFlowElement, String processInstanceId) {
        return forecastNextNodes(getOutgoing((T) sourceFlowElement), processInstanceId);
    }

    protected abstract List<? extends FlowElement> getOutgoing(T flowElement);

    /**
     * 计算出口节点,此时的入参 sourceFlowElement 已经不等于泛型类
     *
     * @param sourceFlowElements 当前节点的出口节点集合
     * @param processInstanceId           流程实例
     * @return
     */
    private List<FlowElement> forecastNextNodes(List<? extends FlowElement> sourceFlowElements,
                                                String processInstanceId) {
        if (CollectionUtils.isEmpty(sourceFlowElements)) {
            return Collections.emptyList();
        }
        Map<Class<? extends FlowElement>, List<FlowElement>> elementGroup =
                sourceFlowElements.stream().collect(Collectors.groupingBy(FlowElement::getClass, Collectors.toList()));

        List<FlowElement> result = new ArrayList<>();
        elementGroup.forEach((k, v) -> {
            AbstractForecast forecast = FORECAST_MAP.get(k);
            if (Objects.nonNull(forecast)) {
                result.addAll(forecast.calcRealOutgoingNodes(v, processInstanceId));
            }
        });
        return result;
    }

    /**
     * 由于每个节点的下级节点是多个,且有可能是不同类型, 在预测下级节点时,需要将同类型的节点传递给对应类型的预测器来计算
     *
     * @param flowElements
     * @param processInstanceId
     * @return
     */
    public List<? extends FlowElement> calcRealOutgoingNodes(List<T> flowElements, String processInstanceId) {
        if (!CollectionUtils.isEmpty(flowElements) && flowElements.size() == 1) {
            return flowElements;
        }
        return Collections.emptyList();
    }

    @Override
    public void setConditionEvaluator(FlowableConditionEvaluator conditionEvaluator) {
        this.conditionEvaluator = conditionEvaluator;
    }

    /**
     * 提供给所有实现类的通用调用引擎评估节点可能存在的条件表达式
     *
     * @param exp               表达式
     * @param processInstanceId 实例 ID
     * @return
     * @see SequenceFlowForecasting#calcRealOutgoingNodes(List, String) 参考这里的用法
     */
    protected final Boolean conditionOn(String exp, String processInstanceId) {
        try {
            return conditionEvaluator.conditionOn(exp, processInstanceId);
        } catch (Exception e) {
            log.warn("条件评估发生异常: {}", e.getMessage());
            return false;
        }
    }
}
