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

import com.brilliance.mda.runtime.annotation.Default;
import com.brilliance.mda.runtime.mda.*;
import com.brilliance.mda.runtime.mda.driver.DCR;
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.AbstractRuleEmitter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

public abstract class AbstractAutoCompileEmitter extends AbstractRuleEmitter {
    private static Logger log = LoggerFactory.getLogger(AbstractAutoCompileEmitter.class);


    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);
            log.debug("executeRule {}.{}.{}{}",MdaEnv.getContext().getTransName(),ruleItem.getDotPath(),ruleItem.getRuleType(),ruleItem.getOrder());
            //校验函数头，当是复合型的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, MdaEnv.getContext().getRoot()));
                        break;
                    case RuleItem.CHECK:
                        execCheckItem(ruleItem.getOrder(), ruleItem.getDotPath(), new RuleItemExecutor(ruleItem, this, MdaEnv.getContext().getRoot()));
                        break;
                    case RuleItem.DEFAULT:
                        execDefaultItem(ruleItem.getOrder(), ruleItem.getDotPath(), new RuleItemExecutor(ruleItem, this, MdaEnv.getContext().getRoot()));
                        break;
                    case RuleItem.RULE:
                        execRuleItem(ruleItem.getOrder(), ruleItem.getDotPath(), new RuleItemExecutor(ruleItem, this, MdaEnv.getContext().getRoot()));
                        break;
                }
            } catch (Exception e) {
                log.error("execute Rule Exception",e);
                throw e;
            } finally {
                this.printSlowExecutionLog();
            }
        }
        this.printSlowExecutionLog();
        return true;
    }
    public static boolean verifyMethodHead(IRuleEmitter emitter,RuleItem ruleItem, String target, String ruleType){
        if(ruleItem.getMethod() instanceof Method && ruleType.equals(RuleItem.DEFAULT)){
            Method method = (Method) ruleItem.getMethod();
            Default defaultAno = method.getAnnotation(Default.class);
            if(defaultAno.value()!=null && defaultAno.value().length > 1){
                String firstTarget = defaultAno.value()[0];
                String path = null;;
                if(firstTarget.startsWith(".")){
                    path = firstTarget;
                }else {
                    path = ruleItem.getDotPath()+"."+ firstTarget;
                }
                path = emitter.getRealPath(path);
                //复合声明，仅与第一个路径相等时，才触发
                if(!target.equals(path)){
                    return false;
                }
            }
        }
        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 instanceof RuleExitException){
                throw (RuleExitException) e;
            }else if(e.getCause() instanceof RulePromptException){
                throw (RulePromptException) e.getCause();
            }else if(e instanceof RulePromptException){
                throw (RulePromptException) e;
            }
            else if(e.getCause() instanceof RuleExecuteException){
                throw (RuleExecuteException) e.getCause();
            }else if(e instanceof RuleExecuteException){
                throw (RuleExecuteException) e;
            }
            else
                throw new RuntimeException(e);
        }finally {
            if(!RuleItem.INIT.equals(ruleType)) {
                ctx.popEventPath();
            }
        }
    }

    @Override
    public boolean executeInit(int order) {
        return  invokeRuleList(RuleItem.INIT,null,order);
    }
    //执行Rule
    public boolean executeRule(String target, int order)
    {
        return invokeRuleList(RuleItem.RULE,target,order);
    }
    //执行Check
    public boolean executeCheck( String target, int order)
    {
        target = getRealPath(target);
        String moduleName = getModuleName();
        ModuleRuleContext moduleRuleContext = ModuleAnnotationProcess.cache.get(moduleName);
        if(moduleRuleContext == null){
            return true;
        }
        for(Map.Entry<String,List<RuleItem>> entry:moduleRuleContext.getCheckRule().entrySet()){
            if(!checkPathMatch(entry.getKey(),target))
                continue;
            boolean result = invokeRuleList(RuleItem.CHECK,entry.getKey(),order);
            if(!result) {
                log.warn(String.format("execute @Check[%s,%d] return false", entry.getKey(), order));
            }
        }
        return true;
    }
    //执行Default
    public boolean executeDefault( String target, int order) {
        boolean result;
        try {
            result = invokeRuleList(RuleItem.DEFAULT,target,order);
        }catch (Exception ex)
        {
            DCR.emptyDefaultQueue();
            throw new RuleExecuteException("Rule执行异常",ex);
        }finally {
        }
        return result;
    }
    //执行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;
    }
}
