package cn.ibizlab.core.extensions.service;

import cn.ibizlab.core.analysis.domain.DADimension;
import cn.ibizlab.core.extensions.domain.BaseRequest;
import cn.ibizlab.core.extensions.domain.SqlResultsMQMsg;
import cn.ibizlab.core.extensions.util.ExpiryMap;
import cn.ibizlab.core.extensions.util.MsgProducerService;
import cn.ibizlab.core.lite.extensions.domain.FieldObj;
import cn.ibizlab.core.lite.extensions.domain.ModelObj;
import cn.ibizlab.core.rule.domain.ExecLog;
import cn.ibizlab.core.rule.domain.ExecResult;
import cn.ibizlab.core.rule.service.IExecLogService;
import cn.ibizlab.util.dict.CodeItem;
import cn.ibizlab.util.dict.CodeList;
import cn.ibizlab.util.helper.CachedBeanCopier;
import cn.ibizlab.util.helper.DataObject;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service("AnalyseEntityServiceImpl")
@ConditionalOnExpression("''.equals('${cassandra.host:}')")
public class AnalyseSqlServiceImpl extends BaseEntityServiceImpl {

    @Autowired
    private MsgProducerService defaultMQProducerService;

    @Autowired
    @Lazy
    private DABuildExService daBuildExService;

    @Autowired
    protected IExecLogService ruExecLogService;

    @Value("${rocketmq.sendResultsBatchSize:1000}")
    private Integer sendResultsBatchSize;

    @Override
    public void saveResult(ModelObj param, String RULEID,String RULECODE, String RULENAME, String RU_EXECRESULTNAME, FieldObj BUSINESSCAT,
                           Integer RETVALUE, FieldObj KEYVALUEFIELD, FieldObj DOMAINSFIELD,FieldObj DIMFIELD,
                           FieldObj METRICFIELD, FieldObj TIMEFIELD, FieldObj EXT1FIELD, FieldObj EXT2FIELD)
    {
        ExecResult result=new ExecResult();
        result.setRuleId(RULEID);
        result.setRuleName(RULENAME);
        result.setName(RU_EXECRESULTNAME);
        result.setRetValue(RETVALUE);

        if(DOMAINSFIELD==null)
            DOMAINSFIELD=param.getEmpty();
        if(BUSINESSCAT==null)
            BUSINESSCAT=param.getEmpty();
        if(METRICFIELD==null)
            METRICFIELD=param.getEmpty();
        if(TIMEFIELD==null)
            TIMEFIELD=param.getEmpty();
        if(EXT1FIELD==null)
            EXT1FIELD=param.getEmpty();
        if(EXT2FIELD==null)
            EXT2FIELD=param.getEmpty();

        result.setKeyValueField(param.getRowKey());

        result.setBusinessCat(BUSINESSCAT.getValue(String.class,null));

        result.setMetricField(METRICFIELD.getDecimal());

        Timestamp timefield=TIMEFIELD.getValue(Timestamp.class,null);
        if(timefield!=null)
            result.setTimeField(timefield);

        String ext1=EXT1FIELD.getValues();
        if(ext1.length()>330)
            ext1=ext1.substring(0,330)+"...";
        if(!StringUtils.isEmpty(ext1))
            result.setExt1Field(ext1);

        String ext2=EXT2FIELD.getValues();
        if(ext2.length()>330)
            ext2=ext2.substring(0,330)+"...";
        if(!StringUtils.isEmpty(ext2))
            result.setExt2Field(ext2);

        result.setDomainsField(DOMAINSFIELD.getValue(String.class, ""));
        result.setSystemId(DataObject.getStringValue(param.get("systemid"),null));

        if((!StringUtils.isEmpty(result.getRuleId()))&&(!StringUtils.isEmpty(result.getDomainsField()))&&(!StringUtils.isEmpty(result.getKeyValueField()))&&(!ObjectUtils.isEmpty(result.getTimeField()))) {

            List<DADimension> dims=getDims(result.getBusinessCat());
            if(dims!=null) {

                for (DADimension dim : dims) {
                    String codevalue=param.$(dim.getField()).getValue(String.class,"");
                    if(!StringUtils.isEmpty(codevalue))
                    {
                        String dictname=dim.getDict();
                        Object val=codevalue;

                        if(!StringUtils.isEmpty(dictname))
                        {
                            CodeList codeList = this.getCodeListCatalog(dictname);
                            //维度指定代码表时，向上同时为每一个父节点添加一条数据
                            while(true)
                            {
                                CodeItem code= codeList.findCodeItem(val);
                                if(code!=null)
                                {
                                    ExecResult result2 = new ExecResult();
                                    CachedBeanCopier.copy(result, result2);
                                    result2.setDimField(code.getValue().toString());
                                    result2.setId(result2.getDefaultKey(true).toString());
                                    if(result2.getRetValue()==1)
                                        this.addResults(param, Tag_SaveResults, result2);
                                    else
                                        this.addResults(param, Tag_DeleteResults, result2);

                                    //非逐层核算或无父代码项退出
                                    if(dim.getRecursive() == 0 || StringUtils.isEmpty(code.getParent()))
                                    {
                                        break;
                                    }else{
                                        val=code.getParent();
                                    }
                                }
                                else
                                    break;
                            }
                        }else{
                            //维度未指定代码表时，只保存一条数据
                            result.setDimField(codevalue);
                            ExecResult result2 = new ExecResult();
                            CachedBeanCopier.copy(result, result2);
                            result2.setId(result2.getDefaultKey(true).toString());
                            if(result2.getRetValue()==1)
                                this.addResults(param, Tag_SaveResults, result2);
                            else
                                this.addResults(param, Tag_DeleteResults, result2);
                        }
                    }
                }
            }

        }
//        log.debug("saveResult:{},{} cost:{}", param.getRowKey(), RULEID, System.currentTimeMillis() - start);
    }

    /**
     * 添加到新增或删除结果队列
     * @param param
     * @param tag
     * @param result
     */
    protected void addResults(ModelObj param, String tag, ExecResult result){
        List<ExecResult> results;
        if(param.containsKey(tag)){
            results = (List<ExecResult>)param.get(tag);
        }else{
            results = new ArrayList<>();
            param.set(tag, results);
        }
        if(Tag_SaveResults.equals(tag)) {
            results.add(result);
        }else{
            ExecResult result2 = new ExecResult();
            result2.setId(result.getId());
            results.add(result2);
        }
    }

    public ExecLog processAll(BaseRequest msg)
    {
        ExecLog execlog=new ExecLog();
        execlog.setId(msg.getId());
        execlog.setRunBody(msg.toString());

        StringBuilder strErrorInfo=new StringBuilder();
        int count = 0;
        int errorcount = 0;

        String strResId=initRule(msg.getRules(),msg.getBatch());
        if (StringUtils.isEmpty(strResId))
        {
            strErrorInfo.append("准备rule文件错误");
            strErrorInfo.append("\r\n");
            execlog.setRetCode(2);
            execlog.setCnt(count);
            execlog.setSucc(errorcount);
            execlog.setRunResult(strErrorInfo.toString());
            ruExecLogService.update(execlog);
            return execlog;
        }

        //构建结果消息对象
        SqlResultsMQMsg resultsMQMsg = new SqlResultsMQMsg();
        resultsMQMsg.setMsgId(msg.getId());
        if(!StringUtils.isEmpty(msg.getResultDataSource())){
            resultsMQMsg.setResultDataSource(msg.getResultDataSource());
        }
        if(!StringUtils.isEmpty(msg.getResultTableName())){
            resultsMQMsg.setResultTableName(msg.getResultTableName());
        }
        List<ExecResult> saveDatas = new ArrayList<>();
        List<ExecResult> deleteDatas = new ArrayList<>();
        try
        {
            for (ModelObj modelObj : msg.getDatas())
            {
                try
                {
                    count++;
                    modelObj.set("BATCH",msg.getBatch());
                    modelObj.set(Tag_EngineId, msg.getEngineId());
                    modelObj.set("systemid",msg.getSystemid());
                    if(!StringUtils.isEmpty(msg.getResultDataSource())){
                        modelObj.set("resultDataSource", msg.getResultDataSource());
                    }
                    if(!StringUtils.isEmpty(msg.getResultTableName())){
                        modelObj.set("resultTableName", msg.getResultTableName());
                    }
                    processRule(modelObj, modelObj.getRowKey(),strResId);

                    //当前业务数据构建结果添加到保存和删除队列
                    Object saveResults = modelObj.get(Tag_SaveResults);
                    if(saveResults != null && saveResults instanceof List){
                        saveDatas.addAll((List<ExecResult>)saveResults);
                    }
                    Object deleteResults = modelObj.get(Tag_DeleteResults);
                    if(deleteResults != null && deleteResults instanceof List){
                        deleteDatas.addAll((List<ExecResult>)deleteResults);
                    }
                    if (saveDatas.size() > sendResultsBatchSize || deleteDatas.size() > sendResultsBatchSize) {
                        resultsMQMsg.setSaveDatas(saveDatas);
                        resultsMQMsg.setDeleteDatas(deleteDatas);
                        defaultMQProducerService.sendSqlResultsMsg(resultsMQMsg);
                        resultsMQMsg.setMsgId(IdWorker.getIdStr());
                        saveDatas.clear();
                        deleteDatas.clear();
                    }
                }
                catch (Exception e)
                {
                    errorcount++;
                    log.error("Service错误，加载数据详细信息：" + modelObj.getRowKey() + "," + e.getMessage());
                    if (strErrorInfo.toString().length() <= 5000)
                    {
                        strErrorInfo.append("加载数据详细信息：" + modelObj.getRowKey() + ",错误：" + e.getMessage());
                        strErrorInfo.append("\r\n");
                        if (e.getCause() != null && e.getCause().getMessage() != null)
                        {
                            strErrorInfo.append(e.getCause().getMessage());
                            strErrorInfo.append("\r\n");
                        }
                    }
                }
            }

            //发送构建结果消息
            if(saveDatas.size() > 0 || deleteDatas.size() > 0) {
                resultsMQMsg.setSaveDatas(saveDatas);
                resultsMQMsg.setDeleteDatas(deleteDatas);
                defaultMQProducerService.sendSqlResultsMsg(resultsMQMsg);
            }
        }
        catch(Exception e)
        {
            log.error("Service读取BaseRequest错误:" + e.toString());
            strErrorInfo.append("读取BaseRequest错误:" + e.getMessage());
            strErrorInfo.append("\r\n");
            if (e.getCause() != null && e.getCause().getMessage() != null)
            {
                strErrorInfo.append(e.getCause().getMessage());
                strErrorInfo.append("\r\n");
            }


            execlog.setRetCode(1);
            execlog.setCnt(count);
            if (errorcount == 0)
                execlog.setSucc(0);
            else
                execlog.setSucc(count-errorcount);
            execlog.setRunResult(strErrorInfo.toString());
            ruExecLogService.update(execlog);
            return execlog;
        }
        finally
        {
            if(setkieSession.containsKey(strResId))
            {
                KieSession kieSession=setkieSession.get(strResId);
                try
                {
                    kieSession.dispose();
                }
                catch (Exception ex){}

                setkieSession.remove(strResId);
                setRuleCount.remove(strResId);
            }

        }

        log.info(new StringBuilder().append("Service成功，process successfully:").append(msg.toString()).toString());
        strErrorInfo.append("Service成功，process successfully\r\n");

        execlog.setCnt(count);
        execlog.setSucc(count-errorcount);
        if(execlog.getCnt()==0)
            execlog.setRetCode(3);
        else
            execlog.setRetCode(0);
        execlog.setRunResult(strErrorInfo.toString());
        this.ruExecLogService.update(execlog);
        return execlog;
    }

    private ExpiryMap<String,List<DADimension>> dimlistMap=new ExpiryMap<>();

    public List<DADimension>  getDims(String id)
    {
        List<DADimension> dims=dimlistMap.get("dims"+id);
        if(dims==null)
        {
            dims=daBuildExService.get(id).getDadimension();
            if(dims!=null)
                dimlistMap.put("dims"+id,dims);
        }
        return dims;
    }
}
