package cn.ibizlab.core.extensions.service;

import cn.ibizlab.core.extensions.domain.BaseRequest;
import cn.ibizlab.core.extensions.domain.EngineMQMsg;
import cn.ibizlab.core.lite.extensions.domain.EntityModel;
import cn.ibizlab.core.lite.extensions.domain.EntityObj;
import cn.ibizlab.core.lite.extensions.domain.FieldModel;
import cn.ibizlab.core.lite.extensions.domain.Setting;
import cn.ibizlab.core.lite.extensions.filter.DbEntitySearchContext;
import cn.ibizlab.core.lite.extensions.model.DataModel;
import cn.ibizlab.core.lite.extensions.service.DbEntityService;
import cn.ibizlab.core.lite.extensions.service.LiteModelService;
import cn.ibizlab.core.lite.service.IMetaModelService;
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.IRuleItemService;
import cn.ibizlab.core.rule.service.impl.RuleEngineServiceImpl;
import cn.ibizlab.util.filter.QueryFilter;
import cn.ibizlab.util.helper.CachedBeanCopier;
import com.alibaba.fastjson.JSON;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
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.apache.ibatis.session.SqlSessionFactory;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.mybatis.spring.batch.MyBatisCursorItemReader;
import org.springframework.batch.item.ExecutionContext;
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 IMetaModelService metaModelService;

    @Autowired
    private DstDataSourceExService dstDataSourceService;

    @Autowired
    @Lazy
    DefaultMQProducer defaultMQProducer;

    @Autowired
    @Lazy
    private SqlSessionFactory sqlSessionFactory;

    @Value("${rocketmq.producer.ruleEngineTopic: DSTMSG}")
    private String ruleEngineTopic;

    @Value("${rocketmq.producer.ruleEngineBatchSize: 500}")
    private int ruleEngineBatchSize;

    @Value("${ibiz.rulepath:/app/file/rules/}")
    private String rulePath;
    /**
     * [Run:运行] 行为扩展
     * @param et
     * @return
     */
    @Override
    //@Transactional
    public RuleEngine run(RuleEngine et) {

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

            BaseRequest msg=new BaseRequest();
            msg.setId(IdWorker.getIdStr());
            msg.setModel(et.getModelName());
            msg.setEngineId(et.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);
            }
            java.sql.Timestamp starttime = new java.sql.Timestamp(System.currentTimeMillis());
            if(true){
                DataModel dataModel= JSON.toJavaObject(JSON.parseObject(metaModelService.get(et.getModelId()).getConfig()), DataModel.class);
                EntityModel entityModel=dataModel.getFactEntityModel();
                FieldModel lastModifyField=entityModel.getLastModifyField();
                QueryFilter filter=new QueryFilter();
                if(lastModifyField!=null)
                    filter.ge(lastModifyField.getColumnName(), et.getLastRuntime());

                //记录处理的总业务数据量
                String sql=entityModel.getSqlSegment("CORE");
                Integer total = dbEntityService.selectCount(entityModel, filter);
                if(total != null){
                    et.setTotal(total);
                    et.setProcessed(0);
                    this.update(et);
                }

                MyBatisCursorItemReader myMyBatisCursorItemReader =new MyBatisCursorItemReader();
                try{
                    myMyBatisCursorItemReader.setSqlSessionFactory(sqlSessionFactory);
                    myMyBatisCursorItemReader.setQueryId("cn.ibizlab.core.extensions.mapper.DbEntityMapper.search");

                    DbEntitySearchContext context=new DbEntitySearchContext();
                    context.setFilter(filter);
                    QueryWrapper qw=context.getSelectCond();
                    if(!StringUtils.isEmpty(filter.getCustSqlSegment()))
                        qw.apply(filter.getCustSqlSegment());

                    Map<String, Object> paramsMap = new HashMap<>();
                    paramsMap.put("sql", sql);
                    paramsMap.put("ew", qw);
                    myMyBatisCursorItemReader.setParameterValues(paramsMap);// 设置sql传入参数

                    dstDataSourceService.initDataSource(entityModel.getDsName());
                    DynamicDataSourceContextHolder.push(entityModel.getDsName());
                    myMyBatisCursorItemReader.open(new ExecutionContext()); // 开启游标
                    DynamicDataSourceContextHolder.poll();
                    List<EntityObj> datas = new ArrayList<>();
                    EntityObj rowdata;
                    while ((rowdata = (EntityObj) myMyBatisCursorItemReader.read()) != null) {
                        datas.add(rowdata);
                        total ++;
                        if(datas.size() >= ruleEngineBatchSize){
                            EngineMQMsg engineMQMsg = new EngineMQMsg();
                            engineMQMsg.setEngineId(et.getEngineId());
                            engineMQMsg.setBatch(msg.getBatch());
                            engineMQMsg.setRunTime(starttime);
                            engineMQMsg.setCount(datas.size());
                            engineMQMsg.setDatas(datas);
                            this.sendToMQ(engineMQMsg);
                            datas.clear();
                        }
                    }
                    if(datas.size() > 0){
                        EngineMQMsg engineMQMsg = new EngineMQMsg();
                        engineMQMsg.setEngineId(et.getEngineId());
                        engineMQMsg.setBatch(msg.getBatch());
                        engineMQMsg.setRunTime(starttime);
                        engineMQMsg.setCount(datas.size());
                        engineMQMsg.setDatas(datas);
                        this.sendToMQ(engineMQMsg);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        myMyBatisCursorItemReader.close();// 关闭游标
                    } catch (Exception ex) {
                        //log.error(ex.getMessage());
                    }
                    DynamicDataSourceContextHolder.poll();
                }

            }else{

                if(!StringUtils.isEmpty(et.getModelId()))
                {

                    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(dbEntityService.getModelObjs(et.getModelId(),fillpropertys,et.getLastRuntime()));



                    ExecLog execlog = baseEntityService.processAll(msg);

                    if (execlog.getRetCode() != 0)
                        return et;
                    et.setLastRuntime(starttime);
                    this.update(et);

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

    protected void sendToMQ(EngineMQMsg engineMQMsg) throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
        String msg = JSON.toJSONString(engineMQMsg);
        Message sendMsg = new Message(ruleEngineTopic, "default", msg.getBytes());
        SendResult sendResult = defaultMQProducer.send(sendMsg);
        log.info("消息发送响应：" + sendResult.toString());
    }

    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);
            }
            java.sql.Timestamp starttime = new java.sql.Timestamp(System.currentTimeMillis());

            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(dbEntityService.getModelObjs(et.getModelId(),fillpropertys,engineMQMsg.getDatas()));

            ExecLog execlog = baseEntityService.processAll(msg);

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

        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 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.run(et);
    }
}

