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

import cd.casic.ci.process.dto.req.pipeline.PipelineQueryReq;
import cd.casic.ci.process.dto.resp.context.TreeRunContextResp;
import cd.casic.ci.process.engine.constant.EngineRuntimeConstant;
import cd.casic.ci.process.engine.enums.ContextStateEnum;
import cd.casic.ci.process.engine.manager.LoggerManager;
import cd.casic.ci.process.engine.manager.PostHandlerManager;
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.dao.history.PipPipelineHisInstanceDao;
import cd.casic.ci.process.process.dao.pipeline.PipTaskDao;
import cd.casic.ci.process.process.dataObject.base.PipBaseElement;
import cd.casic.ci.process.process.dataObject.history.PipPipelineHisInstance;
import cd.casic.ci.process.process.dataObject.pipeline.PipPipeline;
import cd.casic.ci.process.process.service.pipeline.PipelineService;
import cd.casic.ci.process.util.snowflake.SnowflakeIdentifierGenerator;
import cd.casic.framework.commons.exception.ServiceException;
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
import cd.casic.framework.security.core.LoginUser;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
@Slf4j
public class DefaultRunContextManager implements RunContextManager {
    private final Map<String,PipelineRunContext> contextMap= new ConcurrentHashMap();
    @Resource
    private LoggerManager loggerManager;
    @Resource
    private PipPipelineHisInstanceDao hisInstanceDao;
    @Resource
    private PipelineService pipelineService;
    @Resource
    private PostHandlerManager postHandlerManager;
    @Resource
    private SnowflakeIdentifierGenerator idWork;

    @Override
    public Boolean stopPipeline(String pipelineId) {
        return null;
    }

    @Override
    public Boolean notifyPipeline(String pipelineId) {
        return null;
    }

    @Override
    public Boolean suspendPipeline(String pipelineId) {
        return null;
    }

    @Override
    public Boolean notifyStage(String pipelineId, String stageId) {
        return null;
    }

    @Override
    public Boolean suspendStage(String pipelineId, String stageId) {
        PipelineRunContext pipelineRunContext = contextMap.get(pipelineId);
        if (pipelineRunContext !=null) {

        }
        return null;
    }

    @Override
    public void contextRegister(BaseRunContext context) {
        PipBaseElement contextDef = context.getContextDef();
        String id = contextDef.getId();
        BaseRunContext parentContext = context.getParentContext();
        if (context instanceof PipelineRunContext pipelineRunContext) {
            if (contextMap.containsKey(id)) {
                PipelineRunContext oldPipeline = contextMap.get(id);
                if (!ContextStateEnum.HAPPY_ENDING.getCode().equals(oldPipeline.getState().get())
                        &&!ContextStateEnum.BAD_ENDING.getCode().equals(oldPipeline.getState().get())) {
                    throw new ServiceException(GlobalErrorCodeConstants.PIPELINE_ERROR.getCode(),"有未完成执行的流水线请稍后再试");
                }
            }
            pipelineRunContext.setLoggerManager(loggerManager);
            pipelineRunContext.setHisInstanceDao(hisInstanceDao);
            SecurityContext securityContext = SecurityContextHolder.getContext();
            if (securityContext !=null) {
                Authentication authentication = securityContext.getAuthentication();
                if (authentication!=null) {
                    if (authentication.getPrincipal() instanceof LoginUser user) {
                        pipelineRunContext.setUserId(String.valueOf(user.getId()));
                        pipelineRunContext.setNickName(user.getInfo().get("nickname"));
                    }
                }

            }

            pipelineRunContext.setContextManager(this);
            contextMap.put(id,pipelineRunContext);
        } else {
            if (parentContext==null) {
                throw new ServiceException(GlobalErrorCodeConstants.PIPELINE_ERROR.getCode(),"注册context失败");
            }
            PipBaseElement parentContextDef = parentContext.getContextDef();
            BaseRunContext parentRunContext = null;
            for (Map.Entry<String, PipelineRunContext> entry : contextMap.entrySet()) {
                PipelineRunContext value = entry.getValue();
                BaseRunContext runContext = value.getRunContext(parentContextDef.getId());
                if (runContext!=null) {
                    parentRunContext = runContext;
                    break;
                }
            }
            if (parentRunContext==null) {
                throw new ServiceException(GlobalErrorCodeConstants.PIPELINE_ERROR.getCode(),"未找到父级context");
            }
            parentRunContext.putChildRunContext(id,context);
        }
    }

    @Override
    public BaseRunContext getContext(String key) {
        for (Map.Entry<String, PipelineRunContext> entry : contextMap.entrySet()) {
            PipelineRunContext value = entry.getValue();
            BaseRunContext runContext = value.getRunContext(key);
            if (runContext!=null) {
                return runContext;
            }
        }
        return null;
    }
    public void changePipelineState(String pipelineId,ContextStateEnum stateEnum){
        PipelineRunContext pipelineRunContext = contextMap.get(pipelineId);
        if (pipelineRunContext==null) {
            return;
        }
        pipelineRunContext.changeContextStateAndChild(stateEnum);
    }
    // 记录日志需要新事务，成功或失败不影响流水线执行
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    public void toHistory(String pipelineId){
        log.info("========================开始入库");
        BaseRunContext context = getContext(pipelineId);
        PipBaseElement contextDef = context.getContextDef();
        if (context instanceof PipelineRunContext pipelineRunContext) {
            if (contextDef instanceof PipPipeline pipeline){
                Collection<BaseRunContext> values = pipelineRunContext.getChildContext().values();
                List<TaskRunContext> taskContextList = new LinkedList<>();
                for (BaseRunContext value : values) {
                    Collection<BaseRunContext> childContext = value.getChildContext().values();
                    List<TaskRunContext> list = childContext.stream().map(it -> (TaskRunContext) it).toList();
                    taskContextList.addAll(list);
                }
                loggerManager.flushMemory(taskContextList);
                PipPipelineHisInstance pipPipelineHisInstance = new PipPipelineHisInstance();
                pipPipelineHisInstance.setPipelineId(pipeline.getId());
                pipPipelineHisInstance.setPipelineName(pipeline.getName());
                AtomicInteger state = pipelineRunContext.getState();
                pipPipelineHisInstance.setState(String.valueOf(state.get()));
                pipPipelineHisInstance.setStateName(ContextStateEnum.getByCode(state.get()).getMsg());
                pipPipelineHisInstance.setStartTime(pipelineRunContext.getStartTime());
                pipPipelineHisInstance.setExecutorUserId(pipelineRunContext.getUserId());
                pipPipelineHisInstance.setExecutorUserName(pipelineRunContext.getNickName());
                pipPipelineHisInstance.setEndTime(pipelineRunContext.getEndTime());
                PiplineTriggerModeEnum triggerMode = pipelineRunContext.getTriggerMode();
                pipPipelineHisInstance.setTriggerMode(triggerMode.getCode());
                pipPipelineHisInstance.setContextTree(JSON.toJSONString(pipelineRunContext,SerializerFeature.DisableCircularReferenceDetect));
                PipelineQueryReq pipelineQueryReq = new PipelineQueryReq();
                pipelineQueryReq.setId(pipelineId);
                pipPipelineHisInstance.setDefTree(JSON.toJSONString(pipelineService.findPipelineById(pipelineQueryReq), SerializerFeature.DisableCircularReferenceDetect));
                TreeRunContextResp pipelineRunState = pipelineService.getPipelineRunState(pipelineId);
                Map<String, TaskRunContext> collect = taskContextList.stream().collect(Collectors.toMap(item -> item.getContextDef().getId(), Function.identity()));
                for (TreeRunContextResp treeRunContextResp : pipelineRunState.getTaskList()) {
                    if (collect.containsKey(treeRunContextResp.getId())) {
                        Map<String, Object> localVariables = collect.get(treeRunContextResp.getId()).getLocalVariables();
                        Object logId = localVariables.get(EngineRuntimeConstant.HIS_LOG_KEY);
                        treeRunContextResp.setLogId(String.valueOf(logId));
                    }
                }
                pipPipelineHisInstance.setStateTree(JSON.toJSONString(pipelineRunState,SerializerFeature.DisableCircularReferenceDetect));
                pipPipelineHisInstance.setTargetVersionName("");
                pipPipelineHisInstance.setTargetVersionId(pipeline.getTargetVersionId());
                hisInstanceDao.insert(pipPipelineHisInstance);
                // 执行后置处理逻辑
                postHandlerManager.executePostHandler(pipPipelineHisInstance);
            }
        }
    }

}
