package cn.ibizlab.core.extensions.service;

import cn.ibizlab.core.analysis.domain.DABuild;
import cn.ibizlab.core.analysis.domain.DADimension;
import cn.ibizlab.core.analysis.domain.DAMetric;
import cn.ibizlab.core.analysis.service.IDAMetricService;
import cn.ibizlab.core.analysis.service.impl.DABuildServiceImpl;
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.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.RuleItem;
import cn.ibizlab.core.rule.service.IExecLogService;
import cn.ibizlab.core.rule.service.IRuleItemService;
import cn.ibizlab.util.errors.BadRequestAlertException;
import cn.ibizlab.util.helper.CachedBeanCopier;
import cn.ibizlab.util.helper.Setting;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
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.Assert;
import org.springframework.util.StringUtils;

import java.io.File;
import java.sql.Timestamp;
import java.util.*;


/**
 * 实体[分析] 自定义服务对象
 */
@Slf4j
@Primary
@Service("DABuildExService")
public class DABuildExService extends DABuildServiceImpl {

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

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

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

    @Autowired
    protected IExecLogService ruExecLogService;

    @Autowired
    private IDAMetricService idaMetricService;

    @Autowired
    private IRuleItemService ruleItemService;

    @Autowired
    private MsgProducerService defaultMQProducerService;

    @Autowired
    private LiteModelService liteModelService;

    @Autowired
    @Lazy
    private LiteDataService liteDataService;

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


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

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

            idaMetricService.list(Wrappers.<DAMetric>lambdaQuery()
                    .eq(DAMetric::getBuildId,et.getBuildId()))
                    .forEach(dametric -> {
                        idaMetricService.update(dametric);

                    });
        }
        return super.check(et);
    }
    /**
     * [ExportFile:导出文件] 行为扩展：导出模型的文件
     * @param et
     * @return
     */
    @Override
    @Transactional
    public DABuild exportFile(DABuild et) {
        return super.exportFile(et);
    }
    /**
     * [Run:运行] 行为扩展
     * @param et
     * @return
     */
    @Override
    public DABuild run(DABuild et) {
        if(!StringUtils.isEmpty(et.getBuildId()))
        {
            Object timeHour = et.get("hour");
            Object timeDay =  et.get("day");
            CachedBeanCopier.copy(get(et.getBuildId()), et);
            if (!StringUtils.isEmpty(timeHour) || !StringUtils.isEmpty(timeDay)) {
                Calendar c = Calendar.getInstance();
                c.setTime(et.getLastRuntime());
                if (!StringUtils.isEmpty(timeHour)) {
                    Integer hour = Integer.parseInt(String.valueOf(timeHour));
                    c.add(Calendar.HOUR, hour);
                }
                if (!StringUtils.isEmpty(timeDay)) {
                    Integer day = Integer.parseInt(String.valueOf(timeDay));
                    c.add(Calendar.DAY_OF_MONTH, day);
                }
                et.setLastRuntime(new Timestamp(c.getTime().getTime()));
            }
            if("RUNNING".equalsIgnoreCase(et.getState()))
                throw new BadRequestAlertException("构建正在执行中，不能重复执行","DABuild",et.getBuildId());

            BaseRequest msg=new BaseRequest();
            msg.setId(IdWorker.getIdStr());
            msg.setModel(et.getModelName());
            msg.setEngineId(et.getBuildId());
            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.getBuildId());
                    engineMQMsg.setBatch(msg.getBatch());
                    engineMQMsg.setRunTime(starttime);
                    engineMQMsg.setCount(datas.size());
                    engineMQMsg.setDatas(datas);
                    try {
                        defaultMQProducerService.sendBuildMsg(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);
    }

    public DABuild filterData(DABuild et) {
        if(!StringUtils.isEmpty(et.getBuildId()))
        {
            CachedBeanCopier.copy(get(et.getBuildId()), et);
            String metricIds = Setting.getValue(et.getExtParams(), "metricIds");
            String primaryKey = Setting.getValue(et.getExtParams(), "primaryKeys");
            if("RUNNING".equalsIgnoreCase(et.getState()))
                throw new BadRequestAlertException("构建正在执行中，不能重复执行","DABuild",et.getBuildId());
            if (StringUtils.isEmpty(metricIds)) {
                throw new BadRequestAlertException("扩展参数指标参数为空","DABuild",et.getBuildId());
            }
            BaseRequest msg=new BaseRequest();
            msg.setId(IdWorker.getIdStr());
            msg.setModel(et.getModelName());
            msg.setEngineId(et.getBuildId());
            String extCond = Setting.getValue(et.getExtParams(), "extCond");

            if (StringUtils.isEmpty(primaryKey)) {
                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.getBuildId());
                        engineMQMsg.setBatch(msg.getBatch());
                        engineMQMsg.setRunTime(starttime);
                        engineMQMsg.setRuleIds(metricIds);
                        engineMQMsg.setCount(datas.size());
                        engineMQMsg.setDatas(datas);
                        try {
                            defaultMQProducerService.sendBuildMsg(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;
                    }
                });
            } else {
                String state = "FINISH";
                JSONArray jsonArray = JSONArray.parseArray(primaryKey);
                int total = jsonArray.size();
                if(total > 0) {
                    state = "RUNNING";
                    msg.setBatch(msg.getBatch()+"-cnt-"+total);
                }
                et.setTotal(total);
                et.setProcessed(0);
                et.setState(state);
                update(et);
                List<EntityObj> datas = new ArrayList<>();
                for (int i = 0; i < jsonArray.size(); i++) {
                    EntityObj entityObj = new EntityObj();
                    JSONObject jsonObject = JSONObject.parseObject(jsonArray.get(i).toString());
                    for (String s : jsonObject.keySet()) {
                        entityObj.set(s,jsonObject.get(s));
                    }
                    datas.add(entityObj);
                }
                java.sql.Timestamp starttime = new java.sql.Timestamp(System.currentTimeMillis());
                EngineMQMsg engineMQMsg = new EngineMQMsg();
                engineMQMsg.setEngineId(et.getBuildId());
                engineMQMsg.setBatch(msg.getBatch());
                engineMQMsg.setRunTime(starttime);
                engineMQMsg.setRuleIds(metricIds);
                engineMQMsg.setCount(total);
                engineMQMsg.setDatas(datas);
                try {
                    defaultMQProducerService.sendBuildMsg(engineMQMsg);
                } catch (Exception ex) {
                    log.error(String.format("指标构建发送业务数据到MQ失败，error：%1$s\n data：%2$s",ex.getMessage(),JSON.toJSONString(engineMQMsg)));
                }
            }
        }
        return super.filterData(et);
    }


    public void processData(EngineMQMsg engineMQMsg){

        try{
            BaseRequest msg = null;
            if (StringUtils.isEmpty(engineMQMsg.getRuleIds())){
                msg=proxy.getRequest(engineMQMsg.getEngineId(),engineMQMsg.getBatch()).copy(true);
            } else {
                msg=proxy.getRequest2(engineMQMsg.getEngineId(),engineMQMsg.getBatch(),engineMQMsg.getRuleIds()).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(),engineMQMsg.getDatas()));

            analyseEntityServiceImpl.processAll(msg);

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

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

        updateSql = "UPDATE ibzdabuild SET lastruntime=#{et.runtime}," +
                "BUILDSTATE='FINISH' WHERE buildid = #{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.<DABuild>lambdaQuery().eq(DABuild::getBuildId,engineMQMsg.getEngineId()).eq(DABuild::getState,"FINISH"))==1)
        {
            proxy.resetRequest(engineMQMsg.getEngineId(),engineMQMsg.getBatch());
            proxy.resetBuild(engineMQMsg.getEngineId());
            proxy.resetDataAnalyseResult();
        }
    }


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

    @Cacheable(value ="dabuild", key = "'batch:'+#p0+#p1+#p2")
    public BaseRequest getRequest2(String id,String batch,String metricIds)
    {
        DABuild 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");
        if(StringUtils.isEmpty(resultDataSource)){
            resultDataSource=Default_ResultDataSource;
        }
        if(StringUtils.isEmpty(resultTableName)){
            resultTableName=Default_ResultTableName;
        }
        msg.setResultDataSource(resultDataSource);
        msg.setResultTableName(resultTableName);


        List<String> ruleIds = new ArrayList<>();
        List<String> rulePaths = new ArrayList<>();
        DataModel dataModel=liteModelService.getDataModel(et.getModelId());
        HashSet<String> fillpropertys=new HashSet<>();

        List<String> metrics = new ArrayList<>();
        if (StringUtils.isEmpty(metricIds)) {
//            idaMetricService.list(Wrappers.<DAMetric>lambdaQuery().eq(DAMetric::getBuildId,et.getBuildId()))
//                    .forEach(daMetric -> {
//                        metrics.add(daMetric.getMetricId());
//                    });
            List<DAMetric> list = idaMetricService.list(Wrappers.<DAMetric>lambdaQuery().eq(DAMetric::getBuildId, et.getBuildId()));
            for (DAMetric daMetric : list) {
                metrics.add(daMetric.getMetricId());
            }
        } else {
            metrics = Arrays.asList(metricIds.split(";"));
        }



        Assert.notEmpty(metrics,"构建数据失败：未配置指标");

        ruleItemService.list(Wrappers.<RuleItem>lambdaQuery().in(RuleItem::getRuleId, metrics))
                .forEach(ruleItem -> {
                    String path=rulePath + ruleItem.getGroup() + File.separator + ruleItem.getRuleId() + ".drl";
                    File file=new File(path);
                    if(!file.exists())
                    {
                        ruleItemService.buildRuleFile(ruleItem);
                    }
                    if(file.exists())
                    {
                        rulePaths.add(path);
                        ruleIds.add(ruleItem.getRuleId());
                    }
                    if((!StringUtils.isEmpty(ruleItem.getCond()))&&ruleItem.getCond().startsWith("[")) {
                        fillpropertys.addAll(JSON.toJavaObject(JSON.parseArray(ruleItem.getCond()), LinkedHashSet.class));
                    }
                });

        Assert.notEmpty(ruleIds,"构建数据失败：无有效规则");


        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.setFillpropertys(fillpropertys);
        msg.setRuleIds(ruleIds);
        msg.setRules(rulePaths);
        return msg;
    }

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

    }

    @CacheEvict( value="dataAnalyse",allEntries = true)
    public void resetDataAnalyseResult()
    {

    }

    @CacheEvict(value ={"dabuild","dabuilddims"}, key = "'row:'+#p0")
    public void resetBuild(String id)
    {

    }

    @Autowired
    @Lazy
    DABuildExService proxy;

    @Cacheable(value ="dabuilddims", key = "'row:'+#p0")
    public List<DADimension>  getDims(String id)
    {
        return this.get(id).getDadimension();
    }


    @Override
    @Transactional
    @CacheEvict(value ={"dabuild","dabuilddims"}, key = "'row:'+#p0.buildId")
    public boolean update(DABuild et) {
        return super.update(et);
    }


}

