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

import com.brilliance.mda.runtime.annotation.Transaction;
import com.brilliance.mda.runtime.mda.IContext;
import com.brilliance.mda.runtime.mda.IModule;
import com.brilliance.mda.runtime.mda.driver.MdaContext;
import com.brilliance.mda.runtime.mda.driver.MdaEnv;
import com.brilliance.mda.runtime.mda.driver.compile.EmitterManager;
import com.brilliance.mda.runtime.mda.driver.compile.component.DefaultAutoCompileEmitter;
import com.brilliance.mda.runtime.mda.driver.compile.component.DefaultAutoCompileScopeEmitter;
import com.brilliance.mda.runtime.mda.driver.compile.component.ModuleRuleContext;
import com.brilliance.mda.runtime.mda.driver.compile.component.RuleItem;
import com.brilliance.mda.runtime.mda.impl.AbstractRuleEmitter;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;

import java.util.*;

@Data
public class TransAnnotationProcessor extends ModuleAnnotationProcess {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    private static List<TransAnnotationProcessor> transAnnotationProcessorsQueue = new LinkedList<>();

    private String transName;
    private String transNameUpper;
    private Class<?> rootClazz;
    private BeanDefinitionRegistry beanDefinitionRegistry;
    private IModule rootIns;
    private boolean isTrans;
    public String getEmitterKey(){
        return "emitter2."+ this.transName;
    }

    public TransAnnotationProcessor(){
        isTrans = true;
    }

    public TransAnnotationProcessor(boolean isTrans){
        this.isTrans = isTrans;
    }


    protected void init(Class<?> rootClass){
        rootClazz = rootClass;
        String transName = rootClass.getSimpleName().toLowerCase();
        if(rootClass.isAnnotationPresent(Transaction.class)){
            Transaction trnAno = rootClass.getAnnotation(Transaction.class);
            if(trnAno.value()!=null && !trnAno.value().isEmpty()){
                transName = trnAno.value();
            }
        }
        this.transName = transName;
        this.transNameUpper = transName.substring(0,1).toUpperCase()+transName.substring(1);
    }
    public void buildTransEmitter(Class<?> rootClass, BeanDefinitionRegistry beanDefinitionRegistry)  {
        try {
            //初始化上下文
            init(rootClass);
            this.beanDefinitionRegistry = beanDefinitionRegistry;
            IModule rootIns = (IModule) rootClass.newInstance();
            IContext ctx = new MdaContext(rootIns);
            this.makeInstance(rootIns,true);
            //变量模型
            walkModule();
            //注册Emitter
            registerEmitterToBeanFactory();
            //合并Rule Path
            mergeTransRules();
            this.getModuleRuleContext().sortRule();
            pushToProcessQueue();

            EmitterManager.loadEmitterIgnoreInfo(rootClass);
        }catch (Exception e){
            log.error("遍历模型发生异常：{}",e);
        }
    }
    public void registerEmitterToBeanFactory() {
        if(isTrans) {
            this.getModuleRuleContext().setTrans(true);
            registerBean(this.getEmitterKey(),DefaultAutoCompileEmitter.class);
        }
        else{
            this.getModuleRuleContext().setTrans(false);
            registerBean(this.getEmitterKey(),DefaultAutoCompileScopeEmitter.class);
        }

    }

    /**
     注册bean
     **/
    private void registerBean( String name, Class<?> beanClass) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(beanClass);
        beanDefinitionBuilder.addConstructorArgValue(rootClazz);
        BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
        beanDefinitionRegistry.registerBeanDefinition(name, beanDefinition);
    }

    /**
     * 合并当前所有rule的路径
     */
    public void mergeTransRules(){
        //利用Emitter开启rule合并
        AbstractRuleEmitter abstractRuleEmitter = (AbstractRuleEmitter)MdaEnv.getBean(this.getEmitterKey());
        //添加Rule系列函数
        Map<String,List<RuleItem>> eventRuleMap = mergeMapRuleItem(abstractRuleEmitter,this.getModuleRuleContext().getEventRule());
        Map<String,List<RuleItem>> checkRuleMap = mergeMapRuleItem(abstractRuleEmitter,this.getModuleRuleContext().getCheckRule());
        Map<String,List<RuleItem>> defaultRuleMap = mergeMapRuleItem(abstractRuleEmitter,this.getModuleRuleContext().getDefaultRule());

        if(isTrans) {
            //交易下的rule做下合并和排序
            this.getModuleRuleContext().setEventRule(eventRuleMap);
            this.getModuleRuleContext().setCheckRule(checkRuleMap);
            this.getModuleRuleContext().setDefaultRule(defaultRuleMap);
            this.getModuleRuleContext().sortRule();
        }else{
            //支持ModuleList的Module，为其创建一个副本，因为后续交易编译还要使用。
            this.getModuleRuleContext().setUseEmitter(true);
            this.getModuleRuleContext().setMergedContext(new ModuleRuleContext(this.getModuleRuleContext().getModuleName(),false));
            this.getModuleRuleContext().getMergedContext().setEventRule(eventRuleMap);
            this.getModuleRuleContext().getMergedContext().setCheckRule(checkRuleMap);
            this.getModuleRuleContext().getMergedContext().setDefaultRule(defaultRuleMap);
            this.getModuleRuleContext().getMergedContext().setInitList(this.getModuleRuleContext().getInitList());
            this.getModuleRuleContext().getMergedContext().sortRule();
        }
    }

    /**
     * 合并rule map，并返回一个副本
     */
    private Map<String,List<RuleItem>> mergeMapRuleItem(AbstractRuleEmitter abstractRuleEmitter, Map<String,List<RuleItem>> ruleMap){
        Map<String,List<RuleItem>> nRuleMap = new LinkedHashMap<>();
        for(Map.Entry<String,List<RuleItem>> entry: ruleMap.entrySet()){
            String target = entry.getKey();
            List<RuleItem> ruleItemList = entry.getValue();
            target = abstractRuleEmitter.getRealPath(target);
            List<RuleItem> nRuleItemList = nRuleMap.get(target);
            if(nRuleItemList == null){
                nRuleItemList = new ArrayList<>();
                nRuleMap.put(target,nRuleItemList);
            }
            nRuleItemList.addAll(ruleItemList);
        }
        return nRuleMap;
    }

    public void pushToProcessQueue(){
        transAnnotationProcessorsQueue.add(this);
    }

    public static List<TransAnnotationProcessor> getProcessQueue(){
        return transAnnotationProcessorsQueue;
    }
}
