package com.brilliance.mda.runtime.mda.driver.compile.component;

import com.brilliance.mda.runtime.mda.IContext;
import com.brilliance.mda.runtime.mda.RuleExecuteException;
import com.brilliance.mda.runtime.mda.RuleExitException;
import com.brilliance.mda.runtime.mda.RulePromptException;
import com.brilliance.mda.runtime.mda.driver.MdaEnv;
import com.brilliance.mda.runtime.mda.driver.compile.processor.ModuleAnnotationProcess;
import com.brilliance.mda.runtime.mda.impl.AbstractScopeEmitter;

import java.util.List;
import java.util.Map;

import static com.brilliance.mda.runtime.mda.driver.compile.component.AbstractAutoCompileEmitter.verifyMethodHead;

public abstract class AbstractAutoCompileScopeEmitter extends AbstractScopeEmitter {

    private boolean executeRuleList(String ruleType,String target,int order){
        return executeRuleList(ruleType,target,order,false);
    }

    private boolean executeRuleList(String ruleType,String target,int order,boolean verifyHead){
        String moduleName = getModuleName();
        ModuleRuleContext moduleRuleContext = ModuleAnnotationProcess.cache.get(moduleName);
        if(moduleRuleContext == null){
            return false;
        }
        if(target != null){
            target = getRealPath(target);
            if(target.charAt(0) == '.' || target.charAt(0) == '\\') {
                target = target.substring(1);
            }
            MdaEnv.getContext().pushEventPath(target);
        }
        List<RuleItem> ruleItemList = null;
        switch (ruleType){
            case RuleItem.INIT:ruleItemList = moduleRuleContext.getInitList();break;
            case RuleItem.RULE:ruleItemList = moduleRuleContext.getEventRule().get(target);break;
            case RuleItem.CHECK:ruleItemList = moduleRuleContext.getCheckRule().get(target);break;
            case RuleItem.DEFAULT:ruleItemList = moduleRuleContext.getDefaultRule().get(target);break;
        }
        if(ruleItemList == null){
            return false;
        }
        //开始执行RuleChain
        for (int i = 0; i < ruleItemList.size(); i++) {
            RuleItem ruleItem = ruleItemList.get(i);
            //校验函数头，当是复合型的rule声明时，需要第一个target为当前target时才会被ExecuteDefaultRule执行
            if(verifyHead && !verifyMethodHead(this,ruleItem,target,ruleType)){
                continue;
            }
            try {
                switch (ruleItem.getRuleType()) {
                    case RuleItem.INIT:
                        execInitItem(ruleItem.getOrder(), ruleItem.getDotPath(), new RuleItemExecutor(ruleItem, this, this.getContext()));
                        break;
                    case RuleItem.CHECK:
                        execCheckItem(ruleItem.getOrder(), ruleItem.getDotPath(), new RuleItemExecutor(ruleItem, this, this.getContext()));
                        break;
                    case RuleItem.DEFAULT:
                        execDefaultItem(ruleItem.getOrder(), ruleItem.getDotPath(), new RuleItemExecutor(ruleItem, this, this.getContext()));
                        break;
                    case RuleItem.RULE:
                        execRuleItem(ruleItem.getOrder(), ruleItem.getDotPath(), new RuleItemExecutor(ruleItem, this, this.getContext()));
                        break;
                }
            } catch (Exception e) {
                log.error("execute Rule Exception",e);
                throw e;
            } finally {
                this.printSlowExecutionLog();
            }
        }
        this.printSlowExecutionLog();
        return true;
    }

    public boolean invokeRuleList(String ruleType,String target,int order){
        return invokeRuleList(ruleType,target,order,false);
    }

    public boolean invokeRuleList(String ruleType,String target,int order,boolean verifyHead){
        IContext ctx = MdaEnv.getContext();
        try {
            ctx.pushEventPath(target);
            return executeRuleList(ruleType,target,order,verifyHead);
        } catch (Exception e) {
            if(e.getCause() instanceof RuleExitException)
                throw (RuleExitException) e.getCause();
            else if(e.getCause() instanceof RulePromptException){
                throw (RulePromptException) e.getCause();
            }else if(e.getCause() instanceof RuleExecuteException){
                throw (RuleExecuteException) e.getCause();
            }
            else
                throw new RuntimeException(e);
        }finally {
            ctx.popEventPath();
        }
    }

    @Override
    public boolean executeInit(int order) {
        return  invokeRuleList(RuleItem.INIT,null,order);
    }

    public boolean executeRule(String target, int order)
    {
        return invokeRuleList(RuleItem.RULE,target,order);
    }
    //执行check
    public boolean executeCheck( String target, int order)
    {
        return invokeRuleList(RuleItem.CHECK,target,order);
    }
    //执行default
    public boolean executeDefault( String target, int order) {
        return invokeRuleList(RuleItem.DEFAULT,target,order);
    }
    //执行Post 的default，因为它需要验证复合声明的头
    public boolean executePostDefault(String target){
        return invokeRuleList(RuleItem.DEFAULT,target,-1,true);
    }

    public String getModuleName(){
        return relatedTransaction().getCanonicalName();
    }

    @Override
    public Map<String, Integer> getDefaultQueue() {
        Map<String,Integer> defaultQueue = super.getDefaultQueue();
        if(defaultQueue.isEmpty()){
            synchronized (defaultQueue){
                if(defaultQueue.isEmpty()){
                    String moduleName = getModuleName();
                    ModuleRuleContext moduleRuleContext = ModuleAnnotationProcess.cache.get(moduleName);
                    //设置默认的DefaultQueue，但要排除ModuleList
                    if(moduleRuleContext != null){
                        moduleRuleContext.getDefaultRule().entrySet().forEach(stringListEntry -> {
                            if(!(stringListEntry.getValue().size() == 1
                                    && stringListEntry.getValue().get(0).getMethodName().equals("executeDefault"))) {
                                defaultQueue.put(stringListEntry.getKey(), 0);
                            }
                        });
                    }
                }
            }
        }
        return defaultQueue;
    }

    @Override
    public boolean executeDefaultAll(){
        String moduleName = getModuleName();
        ModuleRuleContext moduleRuleContext = ModuleAnnotationProcess.cache.get(moduleName);
        if(moduleRuleContext == null){
            return false;
        }
        moduleRuleContext.getDefaultRule().entrySet().forEach(stringListEntry -> {
            executeRuleList(RuleItem.DEFAULT,stringListEntry.getKey(),-1);
        });
        return true;
    }

    @Override
    public boolean executeCheckAll() {
        String moduleName = getModuleName();
        ModuleRuleContext moduleRuleContext = ModuleAnnotationProcess.cache.get(moduleName);
        if(moduleRuleContext == null){
            return false;
        }
        moduleRuleContext.getCheckRule().entrySet().forEach(stringListEntry -> {
            executeRuleList(RuleItem.CHECK,stringListEntry.getKey(),-1);
        });
        return true;
    }
}
