package cn.ibizlab.core.extensions.service;

import cn.ibizlab.core.extensions.domain.BaseRequest;
import cn.ibizlab.core.extensions.domain.EngineMQMsg;
import cn.ibizlab.core.extensions.util.MsgProducerService;
import cn.ibizlab.core.lite.extensions.domain.EntityObj;
import cn.ibizlab.core.rule.domain.ExecResult;
import cn.ibizlab.util.helper.Setting;
import cn.ibizlab.core.lite.extensions.model.DataModel;
import cn.ibizlab.core.lite.extensions.service.LiteDataCallback;
import cn.ibizlab.core.lite.extensions.service.LiteDataService;
import cn.ibizlab.core.lite.extensions.service.LiteModelService;
import cn.ibizlab.core.rule.domain.ExecLog;
import cn.ibizlab.core.rule.domain.RuleEngine;
import cn.ibizlab.core.rule.domain.RuleItem;
import cn.ibizlab.core.rule.service.IExecLogService;
import cn.ibizlab.core.rule.service.IRuleItemService;
import cn.ibizlab.core.rule.service.impl.RuleEngineServiceImpl;
import cn.ibizlab.util.errors.BadRequestAlertException;
import cn.ibizlab.util.helper.CachedBeanCopier;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 实体[规则引擎] 自定义服务对象
 */
@Slf4j
@Primary
@Service("RuleEngineExService")
public class RuleEngineExService extends RuleEngineServiceImpl {

    @Override
    protected Class currentModelClass() {
        return com.baomidou.mybatisplus.core.toolkit.ReflectionKit.getSuperClassGenericType(this.getClass().getSuperclass(), 1);
    }
 

    @Value("${defaultResultDataSource:default}")
    private String Default_ResultDataSource;

    @Value("${defaultResultTableName:IBZRULERESULT}")
    private String Default_ResultTableName;

    @Autowired
    private IRuleItemService ruleItemService;

    @Autowired
    private BaseEntityService baseEntityService;

    @Autowired
    private LiteModelService liteModelService;

    @Autowired
    private MsgProducerService defaultMQProducerService;

    @Value("${ibiz.rulepath:/app/file/rules/}")
    private String rulePath;

    @Autowired
    @Lazy
    private LiteDataService liteDataService;

    @Autowired
    private ExecResultExService execResultExService;

    @Autowired
    @Qualifier("AnalyseEntityServiceImpl")
    private BaseEntityService analyseEntityServiceImpl;

    /**
     * [Run:运行] 行为扩展
     * @param et
     * @return
     */
    @Override
    public RuleEngine run(RuleEngine et) {

        if(!StringUtils.isEmpty(et.getEngineId()))
        {
            CachedBeanCopier.copy(get(et.getEngineId()), et);

            if("RUNNING".equalsIgnoreCase(et.getState()))
                throw new BadRequestAlertException("引擎正在执行中，不能重复执行","RuleEngine",et.getEngineId());

            BaseRequest msg=new BaseRequest();
            msg.setId(IdWorker.getIdStr());
            msg.setModel(et.getModelName());
            msg.setEngineId(et.getEngineId());
            String extCond = Setting.getValue(et.getExtParams(), "extCond");


            java.sql.Timestamp starttime = new java.sql.Timestamp(System.currentTimeMillis());

            liteDataService.processDataModel(et.getModelId(), extCond, et.getLastRuntime(), new LiteDataCallback<List<EntityObj>>() {
                @Override
                public void total(Integer total) {
                    String state = "FINISH";
                    if(total > 0) {
                        state = "RUNNING";
                        msg.setBatch(msg.getBatch()+"-cnt-"+total);
                    }
                    et.setTotal(total);
                    et.setProcessed(0);
                    et.setState(state);
                    update(et);
                }

                @Override
                public boolean processData(List<EntityObj> datas) {
                    EngineMQMsg engineMQMsg = new EngineMQMsg();
                    engineMQMsg.setEngineId(et.getEngineId());
                    engineMQMsg.setBatch(msg.getBatch());
                    engineMQMsg.setRunTime(starttime);
                    engineMQMsg.setCount(datas.size());
                    engineMQMsg.setDatas(datas);
                    try {
                        defaultMQProducerService.sendEngineMsg(engineMQMsg);
                    } catch (Exception ex) {
                        log.error(String.format("规则引擎发送业务数据到MQ失败，error：%1$s\n data：%2$s",ex.getMessage(),JSON.toJSONString(engineMQMsg)));
                        return false;
                    }
                    return true;
                }
            });


        }
        return super.run(et);
    }

    @Override
    public RuleEngine syncRun(RuleEngine et) {
        log.info("引擎同步校验入参：" + JSON.toJSONString(et));
        if (StringUtils.isEmpty(et.getEngineId()) || StringUtils.isEmpty(et.getDatakeys()))
            return super.syncRun(et);
        String dataKeys = et.getDatakeys();
        String ruleIds = et.getRuleids();
        CachedBeanCopier.copy(get(et.getEngineId()), et);
        String batch = BaseEntityServiceImpl.Tag_Batch_SyncRun + "_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new java.util.Date())+"["+et.getModelName()+"]";
        List<EntityObj> lists = JSON.parseArray(dataKeys,EntityObj.class);
        BaseRequest msg = new BaseRequest();
        if (!StringUtils.isEmpty(ruleIds)){
            msg = proxy.getRequest2(et.getEngineId(),batch,ruleIds).copy(true);
        }else {
            msg = proxy.getRequest(et.getEngineId(),batch).copy(true);
        }
        msg.setId(IdWorker.getIdStr());
        ExecLog execlog=new ExecLog();
        execlog.setId(msg.getId());
        execlog.setName(msg.getBatch());
        execlog.setKeyValueField(msg.getModel());
        execlog.setSystemId(msg.getSystemid());
        ruExecLogService.create(execlog);
        msg.setDatas(liteDataService.getModelObjs(msg.getModelId(),msg.getFillpropertys(),lists));
        msg.setResults(new ArrayList<ExecResult>());
        baseEntityService.processAll(msg);
        et.setResult(JSON.toJSONString(msg.getResults()));
        et.setDatakeys(dataKeys);
        et.setRuleids(ruleIds);
        return et;
    }

	@Autowired
	protected IExecLogService ruExecLogService;

    public void processData(EngineMQMsg engineMQMsg){

        try{
            BaseRequest msg=proxy.getRequest(engineMQMsg.getEngineId(),engineMQMsg.getBatch()).copy(true);
            msg.setId(IdWorker.getIdStr());

            ExecLog execlog=new ExecLog();
            execlog.setId(msg.getId());
            execlog.setName(msg.getBatch());
            execlog.setKeyValueField(msg.getModel());
            execlog.setSystemId(msg.getSystemid());
            try {
                if (StringUtils.hasLength(msg.getExtCond())) {
                    msg.setDatas(liteDataService.getModelObjs2(msg.getModelId(), msg.getFillpropertys(), engineMQMsg.getDatas(), msg.getExtCond()));
                } else {
                    msg.setDatas(liteDataService.getModelObjs(msg.getModelId(), msg.getFillpropertys(), engineMQMsg.getDatas()));
                }
                //自动构建引擎，无业务数据时不记录构建日志
                if(msg.isAutoRun() && (msg.getDatas() == null || msg.getDatas().size() == 0)){
                    return;
                }
            }catch (Exception ex){
                String info = String.format("构建数据：%1$s，业务数据加载错误：%2$s", msg, ex.getMessage());
                log.error(info);
                execlog.setRetCode(4);
                execlog.setRunResult(info);
                ruExecLogService.create(execlog);
                return;
            }
            ruExecLogService.create(execlog);
            baseEntityService.processAll(msg);

        }catch (Exception ex){
            log.error(String.format("构建数据错误：%1$s", ex.getMessage()));
            ex.printStackTrace();
        }

        String updateSql = "UPDATE ibzruleengine SET processed=(CASE WHEN processed is null then 0 else processed end+#{et.count}) WHERE engineid = #{et.engineId}";
        HashMap<String, Object> param = new HashMap<>();
        param.put("count", engineMQMsg.getCount());
        param.put("engineId", engineMQMsg.getEngineId());
        this.execute(updateSql, param);

        updateSql = "UPDATE ibzruleengine SET lastruntime=#{et.runtime}," +
                "EXECSTATE='FINISH' WHERE engineid = #{et.engineId} and total <= processed ";
        param = new HashMap<>();
        param.put("runtime", engineMQMsg.getRunTime());
        param.put("engineId", engineMQMsg.getEngineId());
        this.execute(updateSql, param);

        if(this.count(Wrappers.<RuleEngine>lambdaQuery().eq(RuleEngine::getEngineId,engineMQMsg.getEngineId()).eq(RuleEngine::getState,"FINISH"))==1)
        {
            proxy.resetRequest(engineMQMsg.getEngineId(),engineMQMsg.getBatch());
        }
    }

    @Autowired
    @Lazy
    RuleEngineExService proxy;

    @Cacheable(value ="ruleengine", key = "'batch:'+#p0+#p1")
    public BaseRequest getRequest(String id,String batch){
        return getRequest2(id,batch,null);
    }

    @Cacheable(value ="ruleengine", key = "'batch:'+#p0+#p1+#p2")
    public BaseRequest getRequest2(String id,String batch,String ruleIds)
    {
        RuleEngine et = get(id);
        BaseRequest msg=new BaseRequest();
        msg.setModelId(et.getModelId());
        msg.setModel(et.getModelName());
        msg.setBatch(batch);
        msg.setSystemid(et.getSystemId());
        msg.setEngineId(id);
        String resultDataSource = Setting.getValue(et.getExtParams(), "resultDataSource");
        String resultTableName = Setting.getValue(et.getExtParams(), "resultTableName");
        String resultTopic = Setting.getValue(et.getExtParams(), "resultTopic");
        String isAuto = Setting.getValue(et.getExtParams(), "isAuto");
        String extCond = Setting.getValue(et.getExtParams(), "extCond");
        //设置是否为自动构建引擎
        if("Y".equalsIgnoreCase(isAuto)){
            msg.setAutoRun(true);
        }else{
            msg.setAutoRun(false);
        }
        if(StringUtils.isEmpty(resultDataSource)){
            resultDataSource=Default_ResultDataSource;
        }
        if(StringUtils.isEmpty(resultTableName)){
            resultTableName=Default_ResultTableName;
        }
        if(!StringUtils.isEmpty(resultTopic)){
            msg.setResultTopic(resultTopic);
        }
        if(StringUtils.hasLength(extCond)){
            msg.setExtCond(extCond);
        }
        msg.setResultDataSource(resultDataSource);
        msg.setResultTableName(resultTableName);
        List<String> rules = new ArrayList<>();
        List<String> ruleIdList = new ArrayList<>();
        DataModel dataModel=liteModelService.getDataModel(et.getModelId());
        HashSet<String> fillpropertys=new HashSet<>();
        Wrapper<RuleItem> wrappers = Wrappers.<RuleItem>lambdaQuery().eq(RuleItem::getModelId,et.getModelId()).ne(RuleItem::getGroup,"REP").like(RuleItem::getGroup,et.getGroup());
        if (StringUtils.hasLength(ruleIds)){
            Collection<String> collection = new ArrayList<>();
            String array[] = ruleIds.split(";|,");
            for (int i = 0; i < array.length; i++) {
                collection.add(array[i]);
            }
            wrappers = Wrappers.<RuleItem>lambdaQuery().in(RuleItem::getRuleId,collection);
        }
        ruleItemService.list(wrappers).forEach(ruleItem -> {
            ruleIdList.add(ruleItem.getRuleId());
            String path=rulePath + et.getGroup() + File.separator + ruleItem.getRuleId() + ".drl";
            File file=new File(path);
            if(!file.exists())
            {
                ruleItemService.buildRuleFile(ruleItem);
            }
            if(file.exists())
            {
                rules.add(path);
            }
            if((!StringUtils.isEmpty(ruleItem.getCond()))&&ruleItem.getCond().startsWith("["))
                fillpropertys.addAll(JSON.toJavaObject(JSON.parseArray(ruleItem.getCond()),List.class));
        });
        dataModel.getAllProperty().forEach(prop->{
            if(fillpropertys.contains(prop.getPropertyName()))
            {
                DataModel p=prop.getOwnerDataModel().getParentDataModel();
                while (p!=null)
                {
                    fillpropertys.add(p.getFactPorperty().getPropertyName());
                    p=p.getParentDataModel();
                }
            }
        });
        msg.setRules(rules);
        msg.setRuleIds(ruleIdList);
        msg.setFillpropertys(fillpropertys);
        return msg;
    }

    @CacheEvict(value ="ruleengine", key = "'batch:'+#p0+#p1")
    public void resetRequest(String id,String batch)
    {

    }

    /**
     * [Check:校验] 行为扩展
     * @param et
     * @return
     */
    @Override
    @Transactional
    public RuleEngine check(RuleEngine et) {
        if(!StringUtils.isEmpty(et.getEngineId())) {
            CachedBeanCopier.copy(get(et.getEngineId()), et);

            ruleItemService.list(Wrappers.<RuleItem>lambdaQuery()
                    .eq(RuleItem::getModelId,et.getModelId()).ne(RuleItem::getGroup,"REP").like(RuleItem::getGroup,et.getGroup()))
                    .forEach(ruleItem -> {
                        ruleItemService.update(ruleItem);

                    });
        }
        return super.check(et);
    }

    @Override
    public RuleEngine cleanKC(RuleEngine et) {
        baseEntityService.cleanKieContainer();
        analyseEntityServiceImpl.cleanKieContainer();
        return et;
    }
}

