package com.brilliance.mda.support.service;

import com.brilliance.mda.runtime.mda.*;
import com.brilliance.mda.runtime.mda.driver.DCR;
import com.brilliance.mda.runtime.mda.driver.MdaContext;
import com.brilliance.mda.runtime.mda.driver.MdaDriver;
import com.brilliance.mda.runtime.mda.driver.MdaEnv;
import com.brilliance.mda.runtime.mda.impl.AbstractScopeEmitter;
import com.brilliance.mda.runtime.mda.impl.Argument;
import com.brilliance.mda.runtime.mda.util.Modules;
import com.brilliance.mda.runtime.request.BaseVO;
import com.brilliance.mda.runtime.request.CheckedParams;
import com.brilliance.mda.runtime.request.SelectedParams;
import com.brilliance.mda.runtime.response.ResponseSet;
import com.brilliance.mda.support.cache.ICache;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StopWatch;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;

public abstract class AbstractRouteService<V extends BaseVO> {

    Logger logger = LoggerFactory.getLogger(this.getClass());
    private Class<V> vClass;

    public final static String SAVE_PATH = "mtabut.sav";
    public final static String CONFIRM_PATH = "mtabut.usrcon";
    public final static String PEN_PATH = "mtabut.pen";
    public final static String CHEK_PATH = "mtabut.chk";


    @SuppressWarnings("unchecked")
    public AbstractRouteService() {
        Type t = this.getClass().getGenericSuperclass();
        if (t instanceof ParameterizedType) {
            Type[] ts = ((ParameterizedType) t).getActualTypeArguments();
            vClass = (Class<V>) ts[0];
        }
    }

    public String getServiceName() {
        Service service = this.getClass().getAnnotation(Service.class);
        if (service == null) {
            return null;
        }
        return service.value();
    }

    public IRuleEmitter getEmitter() {
        return getEmitter(getServiceName());
    }

    public IRuleEmitter getEmitterByPath(Argument<String> path) {
        int rightCommasIdx = path.value.lastIndexOf(")");
        if (rightCommasIdx > 0) {
            String modueInsPath = path.value.substring(0, rightCommasIdx + 1);
            IModule module = MdaDriver.getModule(null, modueInsPath);
            AbstractScopeEmitter scopeEmitter = DCR.getModuleListEmitter(module.getClass());
            scopeEmitter.acceptContext(module);
            path.value = path.value.substring(rightCommasIdx + 2);
            return scopeEmitter;
        } else {
            return getEmitter(getServiceName());
        }
    }

    public IRuleEmitter getEmitter(String name) {
        IRuleEmitter emitter = MdaEnv.getBean(IRuleEmitter.EMITTER_PRE + name, IRuleEmitter.class);
        return emitter;
    }

    public IModule createModule() {

        try {
            return (IModule) getEmitter().relatedTransaction().newInstance();
        } catch (Exception e) {
            logger.error("module create error ! ", e);
        }
        return null;
    }

    public V createVO() {
        try {
            return vClass.newInstance();
        } catch (Exception e) {
            logger.error("vo create error ! ", e);
        }
        return null;
    }

    public ResponseSet<V> init(V req) {
        StopWatch stopWatch = new StopWatch("init");
        stopWatch.start("getParams");
        Map<String, Object> params = null;
        if (req != null) {
            params = req.getParams();
            if (req.getParams().containsKey("trn")) {
                return new ResponseSet<V>(req);
            }
        }
        stopWatch.stop();

        stopWatch.start("chain");
        chain(params);
        stopWatch.stop();

        stopWatch.start("initTrans");
        initTrans(req);
        stopWatch.stop();

        stopWatch.start("storeCache");
        IContext ctx = MdaEnv.getContext();
        ICache<String, IContext> cache = MdaEnv.getBean(ICache.CTX_CACHE, ICache.class);
        // String key = cache.store(ctx);
        stopWatch.stop();

        stopWatch.start("createVO");
        V vo = createVO();
        vo.setPageId(cache.generateKey());
        ctx.setVo(vo);
        stopWatch.stop();

        logger.info(stopWatch.prettyPrint());
        return new ResponseSet<>(ctx, vo);
    }

    public ResponseSet<V> checkAll(V req) {
        return executeRule(req, CHEK_PATH);
    }

    @Transactional
    public ResponseSet<V> saveData(V req) {
        return executeRule(req, SAVE_PATH);
    }

    @Transactional
    public ResponseSet<V> confirmData(V req) {
        return executeRule(req, CONFIRM_PATH);
    }

    @Transactional
    public ResponseSet<V> pending(V req) {
        return executeRule(req, PEN_PATH);

    }

    public ResponseSet<V> executeCheck(V req, String... rulePathArr) {
        try {
            StopWatch stopWatch = new StopWatch("executeCheck:" + StringUtils.join(rulePathArr, ','));
            stopWatch.start("enterTransaction");
            enterTransaction(req);
            stopWatch.stop();

            IContext ctx = MdaEnv.getContext();
            synchronized (ctx) {
                stopWatch.start("setValueFromVO");
                setValueFromVO(req);
                stopWatch.stop();

                stopWatch.start("executeStepWithCheckList");
                executeStepWithCheckList(rulePathArr);
                stopWatch.stop();

                stopWatch.start("afterExecute");
                boolean result = ctx.getFieldErrors().size() <= 0;
                afterExecute(rulePathArr);
                stopWatch.stop();

                logger.info(stopWatch.prettyPrint());
                return new ResponseSet<V>(ctx, req);

                //return new ResponseSet<V>(ctx.getErrorNo(), ctx.getMessage(), ctx.getFieldErrors());
            }
        } catch (Exception e) {
            throw new RuleExecuteException(e.getMessage(), e);
        } finally {
            DCR.emptyDefaultQueue();
        }
    }

    public boolean isStdErrorNo(String errorNo) {
        if (errorNo != null && errorNo.matches("E\\d{4}")) {
            return true;
        }
        return false;
    }

    public ResponseSet<V> executeRule(V req, String... rulePathArr) {
        try {
            StopWatch stopWatch = new StopWatch("executeRule:" + StringUtils.join(rulePathArr, ','));
            stopWatch.start("enterTransaction");
            enterTransaction(req);
            stopWatch.stop();

            IContext ctx = MdaEnv.getContext();
            synchronized (ctx) {
                stopWatch.start("setValueFromVO");
                setValueFromVO(req);
                stopWatch.stop();

                stopWatch.start("executeStepWithRuleList");
                boolean[] step = executeStepWithRuleList(rulePathArr);
                stopWatch.stop();

                // checkAll时对ModuleList进行check
                if (rulePathArr.length == 1 && CHEK_PATH.equals(rulePathArr[0])) {
                    stopWatch.start("executeCheckAll");
                    ctx.getEmitter().executeCheckAll();
                    stopWatch.stop();
                }
                boolean result = step[step.length - 1];
                if (!result) {
                    logger.warn("{} execute return false", rulePathArr);
                }

                stopWatch.start("afterExecute");
                afterExecute(rulePathArr);
                stopWatch.stop();

                logger.info(stopWatch.prettyPrint());
                // 机构选择、联行信息
                Map<String, Object> gridData = MdaEnv.getGridData();
                if (gridData != null && rulePathArr.length == 1) {
                    return new ResponseSet<V>(ctx, gridData);
                }
                if (!isStdErrorNo(ctx.getErrorNo()))
                    return new ResponseSet<V>(ctx, req);
                //返回标准错误码消息
                return new ResponseSet<V>(ctx.getErrorNo(), ctx.getErrorMessage(), req);
                //return new ResponseSet<V>(ctx.getErrorNo(), ctx.getErrorMessage(), ctx.getFieldErrors());
            }
        } catch (Exception e) {
            throw new RuleExecuteException(e.getMessage(), e);
//            return new ResponseSet<V>()
        } finally {
            DCR.emptyDefaultQueue();
        }
    }

    /**
     * 执行defaultRule
     */
    public ResponseSet<V> executeDefault(V req, String... rulePathArr) {
        try {
            StopWatch stopWatch = new StopWatch("executeDefault:" + StringUtils.join(rulePathArr, ','));
            stopWatch.start("enterTransaction");
            enterTransaction(req);
            stopWatch.stop();

            IContext ctx = MdaEnv.getContext();
            synchronized (ctx) {
                stopWatch.start("setValueFromVO");
                setValueFromVO(req);
                stopWatch.stop();

                stopWatch.start("executeStepWithDefaultList");
                executeStepWithDefaultList(rulePathArr);
                stopWatch.stop();

                stopWatch.start("afterExecute");
                afterExecute(rulePathArr);
                stopWatch.stop();

                logger.info(stopWatch.prettyPrint());
                return new ResponseSet<V>(ctx, req);
            }
        } catch (Exception e) {
            throw new RuleExecuteException(e.getMessage(), e);
        } finally {
            DCR.emptyDefaultQueue();
        }
    }

    /**
     * 执行Notify
     */
    public ResponseSet<V> executeNotify(V req) {
        try {
            StopWatch stopWatch = new StopWatch("executeNotify");
            stopWatch.start("enterTransaction");
            enterTransaction(req);
            stopWatch.stop();

            IContext ctx = MdaEnv.getContext();
            synchronized (ctx) {
                stopWatch.start("enterTransaction");
                setValueFromVO(req);
                stopWatch.stop();
                //执行Default
                stopWatch.start("executeQueue");
                DCR.executeQueue();
                stopWatch.stop();

                stopWatch.start("executeAndClearPostQueue");
                executeAndClearPostQueue();
                stopWatch.stop();

                //执行Default
                stopWatch.start("executeQueue");
                DCR.executeQueue();
                stopWatch.stop();

                stopWatch.start("afterExecute");
                afterExecute();
                stopWatch.stop();

                logger.info(stopWatch.prettyPrint());
                return new ResponseSet<V>(ctx, req);
            }
        } catch (Exception e) {
            throw new RuleExecuteException(e.getMessage(), e);
        } finally {
            DCR.emptyDefaultQueue();
        }
    }

    /**
     * 初始化步骤
     */
    public void initTrans(V req) {
        StopWatch watch = new StopWatch("initTrans");
        IContext ctx = MdaEnv.getContext();
        IRuleEmitter emitter = getEmitter();
        if (req != null) {
            ctx.setVo(req);
        }
        //将认证信息（usr、usg、ety）放入sysStream
        //后台自动交易可能没有这个授权信息,先这样判断
        IAuthInfo authInfo = MdaEnv.getAuthInfo();
        if (null != authInfo) {
            authInfo.recoveryAuthInfo();
        }
        //一阶段，构造与初始化
//        DCR.emptyDefaultQueue();
        watch.start("executeInit");
        emitter.executeInit(); // 执行初始
        watch.stop();
        DCR.enNotify(true);
        watch.start("executeDefaultAfterInit");
        emitter.executeDefaultAfterInit();
        watch.stop();
        watch.start("executeAndClearPostQueue");
        executeAndClearPostQueue();
        watch.stop();

        logger.info(watch.prettyPrint());
        //执行Default
        DCR.executeQueue();
    }

    /**
     * 切换到交易
     */
    public void chain(Map<String, Object> params) {
        IRuleEmitter emitter = getEmitter();
        IModule m = createModule();
        IContext ctx = new MdaContext(emitter, m);
        ctx.resetParams(params);
    }

    /**
     * 赋值步骤
     */
    public void setVoToTransaction(BaseVO req) {
        IContext ctx = MdaEnv.getContext();
        MdaDriver.setValueFromVO(ctx, req);
        executeAndClearPostQueue();
    }

    /**
     * 执行Rule列表
     */
    public boolean[] executeStepWithRuleList(String... ruleList) {
        boolean[] result = new boolean[ruleList.length];
        for (int i = 0; i < ruleList.length; i++) {
//            DCR.emptyDefaultQueue();
            String path = ruleList[i];
            Argument<String> pathBox = Argument.box(path);
            result[i] = getEmitterByPath(pathBox).executeRule(pathBox.value);
            //todo 判断是否列表数据，例如机构选择，如果是则不执行后续的default，
            Map<String, Object> gridData = MdaEnv.getGridData();
            if (gridData != null && ruleList.length == 1) {
                break;
            }
            //执行Default
            DCR.executeQueue();
            executeAndClearPostQueue();
            //执行Default
            DCR.executeQueue();
        }
        return result;
    }

    /**
     * 执行Check列表
     */
    public boolean[] executeStepWithCheckList(String... ruleList) {
        boolean[] result = new boolean[ruleList.length];
        for (int i = 0; i < ruleList.length; i++) {
//            DCR.emptyDefaultQueue();
            String path = ruleList[i];
            Argument<String> pathBox = Argument.box(path);
            result[i] = getEmitterByPath(pathBox).executeCheck(pathBox.value);
            //执行Default
            DCR.executeQueue();
            executeAndClearPostQueue();
            //执行Default
            DCR.executeQueue();
        }
        return result;
    }

    /**
     * 执行Default列表
     */
    public boolean[] executeStepWithDefaultList(String... ruleList) {
        boolean[] result = new boolean[ruleList.length];
        for (int i = 0; i < ruleList.length; i++) {
//            DCR.emptyDefaultQueue();
            String path = ruleList[i];
            Argument<String> pathBox = Argument.box(path);
            result[i] = getEmitterByPath(pathBox).executeDefault(pathBox.value);
            //执行Default
            DCR.executeQueue();
            executeAndClearPostQueue();
            //执行Default
            DCR.executeQueue();
        }
        return result;
    }


    protected void executeAndClearPostQueue() {
        Modules.executePostQueue();
        Modules.clearPostQueue();
    }


    public boolean beforeExecute() {
        return true;
    }

    public boolean afterExecute(String... rulePathArr) {
        return true;
    }

    public boolean enterTransaction(V req) {
        if (req != null) {
            String pageId = req.getPageId();
            ICache<String, IContext> cache = MdaEnv.getBean(ICache.CTX_CACHE, ICache.class);
            IContext ctx = cache.get(pageId);
            if (ctx == null) {
                Map<String, Object> params = req.getParams();
                chain(params);
                initTrans(req);
                ctx = MdaEnv.getContext();
                ctx.storeData(IContext.DISPLAY_KEY, req);
                // String key = cache.store(ctx);
                req.setPageId(cache.generateKey());
            } else {
                // wgc
                //ctx.clearData();
                ctx.resetParams(req.getParams());
                ctx.setVo(req);
                ctx.getFieldErrors().clear();
                ctx.storeData(IContext.DISPLAY_KEY, req);
                MdaEnv.setContext(ctx);
            }

        }
        return true;
    }

    public void setValueFromVO(V req) {
        DCR.enNotify(true);
        setVoToTransaction(req);
        SelectedParams.selectedParamsInit(req);
        CheckedParams.checkedParamsInit(req);
        //执行Default Queue
//        DCR.executeQueue(false);
        beforeExecute();
    }
}
