package cd.casic.ci.process.engine.runContext;

import cd.casic.ci.process.engine.enums.ContextStateEnum;
import cd.casic.ci.process.process.dataObject.base.PipBaseElement;
import cd.casic.framework.commons.exception.ServiceException;
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.annotation.Transient;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
@Data
@Slf4j
public abstract class BaseRunContext {
    /**
     *  当前上下文的定义
     * */
    protected PipBaseElement contextDef;
    @Transient
    @JsonIgnore
    @JSONField(serialize = false)
    protected BaseRunContext parentContext;
    protected Integer childCount;
    /**
     *  运行状态
     * */
    protected final AtomicInteger state;
    /**
     *  启动时间
     * */
    protected LocalDateTime startTime;
    /**
     *  结束时间
     * */
    protected LocalDateTime endTime;
    protected String resourceId;
    protected String targetVersionId;
    protected String targetType;
    /**
     *  整个流水线全局的变量
     * */
    protected final Map<String,Object> globalVariables;
    /**
     *  当前上下文局部变量
     * */
    protected final Map<String,Object> localVariables;
    protected Map<String,BaseRunContext> childContext;
    /**
     *  用来在控制其他地方的阻塞放行,countDown为1
     * */
    @JsonIgnore
    protected CountDownLatch countDownLatch;

    public BaseRunContext(PipBaseElement contextDef,Integer childCount,BaseRunContext parentContext, LocalDateTime startTime, String resourceId, String targetVersionId, String targetType, Map<String, Object> globalVariables, Map<String, Object> localVariables, Map<String, BaseRunContext> childContext) {
        this.contextDef = contextDef;
        this.childCount = childCount;
        this.parentContext = parentContext;
        this.state = new AtomicInteger(ContextStateEnum.INIT.getCode());
        this.startTime = startTime;
        this.resourceId = resourceId;
        this.targetVersionId = targetVersionId;
        this.targetType = targetType;
        this.globalVariables = globalVariables;
        this.localVariables = localVariables;
        this.childContext = childContext;
    }
    public void changeContextState(ContextStateEnum stateEnum){
        ContextStateEnum curr = ContextStateEnum.getByCode(state.get());
        if (ContextStateEnum.canGoto(curr,stateEnum)) {
            state.compareAndExchange(curr.getCode(),stateEnum.getCode());
            if (ContextStateEnum.HAPPY_ENDING.equals(stateEnum)
                    ||ContextStateEnum.BAD_ENDING.equals(stateEnum)
                    ||ContextStateEnum.SKIP_TO.equals(stateEnum)) {
                this.endTime=LocalDateTime.now();
            }
            callParentChange(stateEnum);
        } else {
            log.error("非法状态扭转直接忽略,{},{}",curr,stateEnum);
        }
    }
    public void changeContextStateAndChild(ContextStateEnum stateEnum){
        ContextStateEnum curr = ContextStateEnum.getByCode(state.get());
        if (ContextStateEnum.canGoto(curr,stateEnum)) {
            state.compareAndExchange(curr.getCode(),stateEnum.getCode());
            Collection<BaseRunContext> values = this.getChildContext().values();
            if (!CollectionUtils.isEmpty(values)) {
                for (BaseRunContext value : values) {
                    value.changeContextStateAndChild(stateEnum);
                }
            }
        }
    }

    /**
     *  获取当前或者子上下文
     * */
    public abstract BaseRunContext getRunContext(String key);
    public abstract void putChildRunContext(String key,BaseRunContext context);
    public void callParentChange(ContextStateEnum state){
        if (parentContext==null) {
            return;
        }
        if (ContextStateEnum.HAPPY_ENDING.equals(state)
                ||ContextStateEnum.BAD_ENDING.equals(state)
                ||ContextStateEnum.SKIP_TO.equals(state)) {
            parentContext.checkChildEnd();
            unpause();
        } else if(ContextStateEnum.READY.equals(state)){
            parentContext.checkChildReady();
        } else if(ContextStateEnum.RUNNING.equals(state)){
            parentContext.checkChildRunning();
        }
        parentContext.callParentChange(state);
    }
    /**
     *  查找子类是否全部完成，如果子类全部完成则父类也完成
     * */
    public void checkChildEnd() throws ServiceException{
        Map<String, BaseRunContext> childContext = getChildContext();

        int result = ContextStateEnum.HAPPY_ENDING.getCode();
        for (Map.Entry<String, BaseRunContext> entry : childContext.entrySet()) {
            BaseRunContext child = entry.getValue();
            int state = child.getState().get();
            if (!ContextStateEnum.HAPPY_ENDING.getCode().equals(state)
                    &&!ContextStateEnum.BAD_ENDING.getCode().equals(state)
                    &&!ContextStateEnum.SKIP_TO.getCode().equals(state)) {
                return;
            }
            // 子成员中有一个失败则状态为失败
            if (ContextStateEnum.BAD_ENDING.getCode().equals(state)) {
                result=state;
            }
        }
        boolean end = false;
        if (ContextStateEnum.HAPPY_ENDING.getCode()==result) {
            if (childContext.size()!=childCount) {
                return;
            }
            if (ContextStateEnum.canGoto(ContextStateEnum.getByCode(state.get()),ContextStateEnum.HAPPY_ENDING)) {
                this.changeContextState(ContextStateEnum.HAPPY_ENDING);
                end = true;
            }
        } else {
            if (ContextStateEnum.canGoto(ContextStateEnum.getByCode(state.get()),ContextStateEnum.BAD_ENDING)) {
                this.changeContextState(ContextStateEnum.BAD_ENDING);
                end = true;
            }
        }
        // 当前执行结束看看是否要执行后置处理
    }
    /**
     *  查找子类是否全部就绪，如果子类全部完成则父类也就绪
     *  TODO 逻辑可能有点问题
     * */
    public void checkChildReady() throws ServiceException{
        int result = ContextStateEnum.READY.getCode();
        for (Map.Entry<String, BaseRunContext> entry : childContext.entrySet()) {
            BaseRunContext child = entry.getValue();
            int state = child.getState().get();
            if (!ContextStateEnum.READY.getCode().equals(state)) {
                return;
            }
            result&=state;
        }
        if (ContextStateEnum.READY.getCode().equals(result)) {
            if (ContextStateEnum.canGoto(ContextStateEnum.getByCode(state.get()),ContextStateEnum.READY)) {
                this.changeContextState(ContextStateEnum.READY);
            }
        }
    }
    /**
     *  查找子类是否存在开始运行的，如果有则父状态变成running
     * */
    public void checkChildRunning() throws ServiceException{
        Boolean runningFlag = false;
        for (Map.Entry<String, BaseRunContext> entry : childContext.entrySet()) {
            BaseRunContext child = entry.getValue();
            int state = child.getState().get();
            if (ContextStateEnum.RUNNING.getCode().equals(state)) {
                runningFlag = true;
                break;
            }
        }
        if (runningFlag) {
            if (ContextStateEnum.canGoto(ContextStateEnum.getByCode(state.get()),ContextStateEnum.RUNNING)) {
                this.changeContextState(ContextStateEnum.RUNNING);
            } else{
                log.error("非正常状态扭转{}->{}",ContextStateEnum.getByCode(state.get()),ContextStateEnum.RUNNING);
//                throw new ServiceException(GlobalErrorCodeConstants.PIPELINE_ERROR.getCode(),"状态有误");
            }
        }
    }
    public void pause(){
        if (countDownLatch==null) {
            synchronized(this) {
                if (this.countDownLatch == null) {
                    this.countDownLatch= new CountDownLatch(1);
                }
            }
        }
        try {
            this.countDownLatch.await();
        } catch (InterruptedException e) {
            log.error(e.getMessage());}
    }
    private void unpause(){
        if (this.countDownLatch!=null) {
            this.countDownLatch.countDown();
            if (this.countDownLatch.getCount()==0) {
                this.countDownLatch=null;
            }
        }
    }

    @Override
    public String toString() {
        return "BaseRunContext{" +
                "contextDef=" + contextDef +
                ", childCount=" + childCount +
                ", state=" + state +
                ", startTime=" + startTime +
                ", endTime=" + endTime +
                ", resourceId='" + resourceId + '\'' +
                ", targetVersionId='" + targetVersionId + '\'' +
                ", targetType='" + targetType + '\'' +
                ", globalVariables=" + globalVariables +
                ", localVariables=" + localVariables +
                ", childContext=" + childContext +
                ", countDownLatch=" + countDownLatch +
                '}';
    }
}
