package cd.casic.ci.process.engine.dispatcher.impl;

import cd.casic.ci.process.engine.constant.PipelineBehaviorConstant;
import cd.casic.ci.process.engine.dispatcher.BaseDispatcher;
import cd.casic.ci.process.engine.enums.ContextStateEnum;
import cd.casic.ci.process.engine.manager.RunContextManager;
import cd.casic.ci.process.engine.runContext.BaseRunContext;
import cd.casic.ci.process.engine.runContext.PipelineRunContext;
import cd.casic.ci.process.engine.runContext.SecondStageRunContext;
import cd.casic.ci.process.engine.runContext.TaskRunContext;
import cd.casic.ci.process.process.dataObject.stage.PipStage;
import cd.casic.framework.mq.redis.core.RedisMQTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
@Slf4j
public class ParallelDispatcher implements BaseDispatcher{

    private List<PipStage> firstStageList;
    private Integer stageIndex;
    private PipelineRunContext pipelineRunContext;
    private RunContextManager runContextManager;
    private RedisMQTemplate redisMQTemplate;
    private ThreadPoolTaskExecutor taskExecutor;

    public ParallelDispatcher(List<PipStage> firstStageList, PipelineRunContext context, RunContextManager contextManager, RedisMQTemplate redisMQTemplate, ThreadPoolTaskExecutor taskExecutor) {
        this.firstStageList = firstStageList;
        this.pipelineRunContext = context;
        this.stageIndex = 0;
        this.runContextManager = contextManager;
        this.redisMQTemplate = redisMQTemplate;
        this.taskExecutor = taskExecutor;
    }

    @Override
    public void dispatch() throws InterruptedException {
        // 负责依次执行阶段
        for (PipStage stage : firstStageList) {
            List<PipStage> stageList = stage.getStageList();
            if (CollectionUtils.isEmpty(stageList)) {
                // 此处可以暂时记录日志
                continue;
            }
            CountDownLatch latch = new CountDownLatch(stageList.size());
            for (PipStage secondStage : stageList) {
                Map<String, Object> globalVariables = pipelineRunContext.getGlobalVariables();
                Object fromError = globalVariables.get(PipelineBehaviorConstant.PIPELINE_EXECUTE_FROM_ERROR);
                SecondStageRunContext context = null;
                if (!Boolean.TRUE.equals(fromError)) {
                    // 注册taskContext，且发送消息至消息队列给work执行, 如果需要则传入参数
                    // 二阶段下所有task是串行所以不用关心线程安全相关信息
                    context = new SecondStageRunContext(secondStage,secondStage.getTaskValues().size(),pipelineRunContext,new ConcurrentHashMap<>());
                    runContextManager.contextRegister(context);
                } else {
                    BaseRunContext baseRunContext = runContextManager.getContext(secondStage.getId());
                    if (baseRunContext!=null) {
                        context = (SecondStageRunContext) baseRunContext;
                        context.setContextDef(secondStage);
                        context.setChildCount(secondStage.getTaskValues().size());
                    } else {
                        // 防止运行停止以后添加节点,以及到达之前未执行的节点 ,todo 删除节点的同时也要删除上下文
                        context = new SecondStageRunContext(secondStage,secondStage.getTaskValues().size(),pipelineRunContext,new ConcurrentHashMap<>());
                        runContextManager.contextRegister(context);
                    }
                }
                SerialDispatcher serialDispatcher = new SerialDispatcher(context,latch,runContextManager,redisMQTemplate);
                // 给线程池进行执行
                taskExecutor.execute(serialDispatcher);
            }
            // 等待当前阶段执行
            latch.await();
            // TODO 检查是否全部执行成功 ，目前没有逻辑就是忽略错误
            // 当前执行失败
            if (pipelineRunContext.getState().get()== ContextStateEnum.BAD_ENDING.getCode()) {
                log.error("并行执行停止");
                break;
            }
        }
    }
    @Override
    public void run() {
        // TODO 计时
        try {
            dispatch();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
