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.DefaultMQProducerService;
import cn.ibizlab.core.extensions.util.MQConsumeMsgListenerProcessor;
import cn.ibizlab.core.lite.extensions.domain.EntityObj;
import cn.ibizlab.core.lite.extensions.domain.Setting;
import cn.ibizlab.core.lite.extensions.model.DataModel;
import cn.ibizlab.core.lite.extensions.service.DbEntityService;
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.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.Value;
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.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);
    }

    public static final String Setting_ResultDataSource = "resultDataSource";
    public static final String Setting_ResultTableName = "resultTableName";

    @Autowired
    private DbEntityService dbEntityService;

    @Autowired
    private IRuleItemService ruleItemService;

    @Autowired
    private BaseEntityService baseEntityService;

    @Autowired
    private LiteModelService liteModelService;

    @Autowired
    private DefaultMQProducerService defaultMQProducerService;

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

    @Autowired
    @Lazy
    private LiteDataService liteDataService;
    /**
     * [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());


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

            liteDataService.processDataModel(et.getModelId(), et.getLastRuntime(), new LiteDataCallback<List<EntityObj>>() {
                @Override
                public void total(Integer total) {
                    String state = "FINISH";
                    if(total > 0)
                        state = "RUNNING";
                    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.sendToMQ(engineMQMsg, MQConsumeMsgListenerProcessor.Type_Engine);
                    } catch (Exception ex) {
                        return false;
                    }
                    return true;
                }
            });


        }
        return super.run(et);
    }



	@Autowired
	protected IExecLogService ruExecLogService;

    public void processData(EngineMQMsg engineMQMsg){
        RuleEngine et = get(engineMQMsg.getEngineId());
        try{
            BaseRequest msg=new BaseRequest();
            msg.setId(IdWorker.getIdStr());
            msg.setModel(et.getModelName());
            msg.setBatch(engineMQMsg.getBatch());
            msg.setSystemid(et.getSystemId());
            msg.setEngineId(engineMQMsg.getEngineId());
            String resultDataSource = Setting.getValue(et.getExtParams(), Setting_ResultDataSource);
            String resultTableName = Setting.getValue(et.getExtParams(), Setting_ResultTableName);
            if(!StringUtils.isEmpty(resultDataSource)){
                msg.setResultDataSource(resultDataSource);
            }
            if(!StringUtils.isEmpty(resultTableName)){
                msg.setResultTableName(resultTableName);
            }
            ExecLog execlog=new ExecLog();
            execlog.setId(msg.getId());
            execlog.setName(msg.getBatch());
            execlog.setKeyValueField(msg.getModel());
            execlog.setSystemId(msg.getSystemid());
            ruExecLogService.create(execlog);

            List<String> rules = new ArrayList<>();
            DataModel dataModel=liteModelService.getDataModel(et.getModelId());
            HashSet<String> fillpropertys=new HashSet<>();
            ruleItemService.list(Wrappers.<RuleItem>lambdaQuery()
                    .eq(RuleItem::getModelId,et.getModelId()).ne(RuleItem::getGroup,"REP").like(RuleItem::getGroup,et.getGroup()))
                    .forEach(ruleItem -> {
                        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()),LinkedHashSet.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.setDatas(liteDataService.getModelObjs(et.getModelId(),fillpropertys,engineMQMsg.getDatas()));

            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}),lastruntime=CASE WHEN total <= processed THEN #{et.runtime} ELSE lastruntime END," +
                "EXECSTATE=CASE WHEN total <= processed THEN 'FINISH' ELSE EXECSTATE END WHERE engineid = #{et.engineId}";
        HashMap<String, Object> param = new HashMap<>();
        param.put("count", engineMQMsg.getCount());
        param.put("runtime", engineMQMsg.getRunTime());
        param.put("engineId", engineMQMsg.getEngineId());
        this.execute(updateSql, param);
    }
    /**
     * [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);
    }
}

