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

import cd.casic.ci.process.engine.constant.PipelineBehaviorConstant;
import cd.casic.ci.process.engine.dispatcher.impl.ParallelDispatcher;
import cd.casic.ci.process.engine.enums.ContextStateEnum;
import cd.casic.ci.process.engine.executor.PipelineExecutor;
import cd.casic.ci.process.engine.manager.LoggerManager;
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.TaskRunContext;
import cd.casic.ci.process.enums.PiplineTriggerModeEnum;
import cd.casic.ci.process.process.dataObject.pipeline.PipPipeline;
import cd.casic.ci.process.process.dataObject.stage.PipStage;
import cd.casic.ci.process.process.service.pipeline.PipelineService;
import cd.casic.ci.process.process.service.stage.StageService;
import cd.casic.ci.process.util.WebFrameworkUtils;
import cd.casic.framework.commons.exception.ServiceException;
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
import cd.casic.framework.mq.redis.core.RedisMQTemplate;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Slf4j
public class DefaultPipelineExecutor implements PipelineExecutor {
    @Resource
    private PipelineService pipelineService;
    @Resource
    private StageService stageService;
    @Resource
    private RunContextManager runContextManager;
    @Resource
    private ThreadPoolTaskExecutor parallelExecutor;
    @Resource
    private ThreadPoolTaskExecutor serialExecutor;
    @Resource
    private RedisMQTemplate redisMQTemplate;
    @Resource
    private LoggerManager loggerManager;
    @Override
    public PipelineRunContext execute(String pipelineId, PiplineTriggerModeEnum triggerModeEnum,Boolean formError) {
        PipPipeline pipeline = pipelineService.getById(pipelineId);
        if (pipeline==null) {
            return null;
        }
        // TODO 判断状态不能重复运行
        Integer state = pipeline.getState();
        // TODO 判断资源是否申请成功是否处于可运行状态
        String resourceId = pipeline.getResourceId();
        String executeStatus = pipeline.getExecuteStatus();
        // TODO 如果判断成功则查询所有的阶段信息
        List<PipStage> mainStage = stageService.findAllFirstStagesAndChild(pipelineId);
        if (CollectionUtils.isEmpty(mainStage)) {
            throw new ServiceException(GlobalErrorCodeConstants.PIPELINE_ERROR.getCode(),"未找到有效阶段信息");
        }
        Integer childCount= 0;
        for (PipStage stage : mainStage) {
            childCount+=stage.getStageList().size();
        }
        PipelineRunContext pipelineRunContext = null;
        if (!formError) {
            // 从头执行清理缓存日志
            loggerManager.clear(pipelineId);
            ConcurrentHashMap<String, Object> globalVariable = null;
            globalVariable = new ConcurrentHashMap<>();
            // 如果要做 容灾就需要重新将数据库存的记录按顺序加载入
            pipelineRunContext=new PipelineRunContext(pipeline,childCount,null,globalVariable,new ConcurrentHashMap<>());
            runContextManager.contextRegister(pipelineRunContext);
        } else {
            try {
                pipelineRunContext = (PipelineRunContext)runContextManager.getContext(pipelineId);
            } catch (Exception e) {
                // 以后可以改成从 数据库重载
                throw new ServiceException(GlobalErrorCodeConstants.PIPELINE_ERROR.getCode(),"未找到上次的记录");
            }
            fromErrorInit(pipelineId);
        }

        pipelineRunContext.setTriggerMode(triggerModeEnum);
        pipelineRunContext.getGlobalVariables().put(PipelineBehaviorConstant.PIPELINE_EXECUTE_FROM_ERROR,formError);


        ParallelDispatcher parallelDispatcher = new ParallelDispatcher(mainStage,pipelineRunContext,runContextManager,redisMQTemplate,serialExecutor);
        parallelExecutor.execute(parallelDispatcher);
        return pipelineRunContext;
    }
    public void fromErrorInit(String pipelineId){
        List<BaseRunContext> allContextList = new LinkedList<>();
        BaseRunContext context = runContextManager.getContext(pipelineId);
        if (context ==null) {
            return;
        }
        if (context instanceof PipelineRunContext pipelineRunContext) {
            pipelineRunContext.setUserId(WebFrameworkUtils.getLoginUserIdStr());
            pipelineRunContext.setUserId(WebFrameworkUtils.getLoginNickName());
            pipelineRunContext.setStartTime(LocalDateTime.now());
        }

        allContextList.add(context);
        Collection<BaseRunContext> stageContextList = context.getChildContext().values();
        allContextList.addAll(stageContextList);
        for (BaseRunContext baseRunContext : stageContextList) {
            Collection<TaskRunContext> taskContextList = baseRunContext.getChildContext().values().stream().map(item->(TaskRunContext)item).toList();
            allContextList.addAll(taskContextList);
        }
        log.info("上下文分组完毕");
        for (BaseRunContext taskRunContext : allContextList) {
            if (ContextStateEnum.BAD_ENDING.getCode().equals(taskRunContext.getState().get()))
                taskRunContext.changeContextState(ContextStateEnum.ERROR_EXECUTE_READY);
        }
    }
    public void traversePipelineContext(String pipelineId){
        BaseRunContext context = runContextManager.getContext(pipelineId);
        Collection<BaseRunContext> stageContextList = context.getChildContext().values();
        List<TaskRunContext> allTaskList = new LinkedList<>();
        for (BaseRunContext baseRunContext : stageContextList) {
            Collection<TaskRunContext> taskContextList = baseRunContext.getChildContext().values().stream().map(item->(TaskRunContext)item).toList();
            allTaskList.addAll(taskContextList);
        }
        log.info("上下文分组完毕");
    }
}
