package cn.ibizlab.core.extensions.service;

import cn.ibizlab.core.extensions.mapper.ExecResultExMapper;
import cn.ibizlab.core.extensions.util.MsgProducerService;
import cn.ibizlab.core.rule.domain.ExecResult;
import cn.ibizlab.core.rule.service.IRuleEngineService;
import cn.ibizlab.core.rule.service.impl.ExecResultServiceImpl;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
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.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.sql.Timestamp;
import java.util.*;

/**
 * 实体[执行结果] 自定义服务对象
 */
@Slf4j
@Primary
@Service("ExecResultExService")
public class ExecResultExService extends ExecResultServiceImpl {

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

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

    @Value("${ibiz.execResult.saveBatchSize:500}")
    private int saveBatchSize;
    @Value("${ibiz.execResult.deleteBatchSize:500}")
    private int deleteBatchSize;
    @Autowired
    private MsgProducerService defaultMQProducerService;

    @Autowired
    @Lazy
    public IRuleEngineService engineService;

    private Map<String, List<ExecResult>> resultsSaveAsync = new HashMap<>();
    private Object objResultSaveAsyncLock = new Object();
    private Timer timerSaveAsync = null;

    private Map<String, List<ExecResult>> resultsDeleteAsync = new HashMap<>();
    private Object objResultDeleteAsyncLock = new Object();
    private Timer timerDeleteAsync = null;

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

    /**
     * [SaveAsync:SaveAsync] 行为扩展
     *
     * @param et
     * @return
     */
    @Override
    @Transactional
    public ExecResult saveAsync(ExecResult et) {
        synchronized (objResultSaveAsyncLock) {
            addExecResult(resultsSaveAsync, et);
        }
        //创建一个定时器
        if (timerSaveAsync == null) {
            timerSaveAsync = new Timer();
            //schedule方法是执行时间定时任务的方法
            timerSaveAsync.schedule(new TimerTask() {
                //run方法就是具体需要定时执行的任务
                @Override
                public void run() {
                    try {
                        synchronized (objResultSaveAsyncLock) {
                            if (resultsSaveAsync.size() > 0) {

                                for (Map.Entry<String, List<ExecResult>> entry : resultsSaveAsync.entrySet()) {
                                    String execResultId = entry.getKey();
                                    String [] idArray = execResultId.split("\\|");
                                    List<ExecResult> execResults = entry.getValue();
                                    List<ExecResult> tempExecResults = new ArrayList<>();
                                    if (execResults.size() > 0 && idArray.length==3) {
                                        //批量操作数据主键排重
                                        LinkedHashMap<Object,ExecResult> params=new LinkedHashMap<>();
                                        execResults.forEach(item->{
                                            params.put(item.getId(),item);
                                        });
                                        for (ExecResult execResult : params.values()) {
                                            tempExecResults.add(execResult);
                                            if (tempExecResults.size() >= saveBatchSize) {
                                                saveResultBatch(tempExecResults, idArray[1] , idArray[2]);
                                                tempExecResults.clear();
                                            }
                                        }
                                        if (tempExecResults.size() > 0) {
                                            saveResultBatch(tempExecResults, idArray[1] , idArray[2]);
                                            tempExecResults.clear();
                                        }
                                        resultsSaveAsync.remove(execResultId);
                                    }
                                }
                            }
                        }
                    } catch (Exception ex) {
                        log.error(ex.getMessage());
                    }
                }
            }, 1000, 10000);
        }
        return et;
    }

    /**
     * [DeleteAsync:DeleteAsync] 行为扩展
     *
     * @param et
     * @return
     */
    @Override
    @Transactional
    public ExecResult deleteAsync(ExecResult et) {
        synchronized (objResultDeleteAsyncLock) {
            addExecResult(resultsDeleteAsync, et);
        }
        if (timerDeleteAsync == null) {
            timerDeleteAsync = new Timer();
            //schedule方法是执行时间定时任务的方法
            timerDeleteAsync.schedule(new TimerTask() {

                //run方法就是具体需要定时执行的任务
                @Override
                public void run() {
                    try {
                        synchronized (objResultDeleteAsyncLock) {
                            if (resultsDeleteAsync.size() > 0) {
                                for (Map.Entry<String, List<ExecResult>> entry : resultsDeleteAsync.entrySet()) {
                                    String execResultId = entry.getKey();
                                    List<ExecResult> execResults = entry.getValue();
                                    List<ExecResult> tempExecResults = new ArrayList<>();
                                    String [] idArray = execResultId.split("\\|");
                                    if (execResults.size() > 0 && idArray.length==3) {
                                        //批量操作数据主键排重
                                        LinkedHashMap<Object,ExecResult> params=new LinkedHashMap<>();
                                        execResults.forEach(item->{
                                            params.put(item.getId(),item);
                                        });
                                        for (ExecResult execResult : params.values()) {
                                            tempExecResults.add(execResult);
                                            if (tempExecResults.size() >= deleteBatchSize) {
                                                clearResultBatch(tempExecResults, idArray[1] , idArray[2]);
                                                tempExecResults.clear();
                                            }
                                        }
                                        if (tempExecResults.size() > 0) {
                                            clearResultBatch(tempExecResults, idArray[1] , idArray[2]);
                                            tempExecResults.clear();
                                        }
                                        resultsDeleteAsync.remove(execResultId);
                                    }
                                }
                            }
                        }
                    } catch (Exception ex) {
                        log.error(ex.getMessage());
                    }
                }
            }, 1000, 10000);
        }
        return et;
    }

    /**
     * 将规则执行结果添加到待保存集合中
     *
     * @param execResultMap
     * @param execResult
     */
    private void addExecResult(Map<String, List<ExecResult>> execResultMap, ExecResult execResult) {
        Object engineId = execResult.get(BaseEntityServiceImpl.Tag_EngineId);
        Object dsName = execResult.get("resultDataSource");
        Object tabName = execResult.get("resultTableName");
        String id = String.format("%s|%s|%s",engineId,dsName,tabName);
        if (ObjectUtils.isEmpty(engineId)) {
            log.error("存储规则结果失败，无法数据[{}]的引擎标识!", execResult.getId());
            return;
        }
        if (execResultMap.containsKey(id)) {
            execResultMap.get(id).add(execResult);
        } else {
            execResultMap.put(id, new ArrayList() {{
                add(execResult);
            }});
        }
    }

    @Autowired
    private DstDataSourceExService dstDataSourceService;


    @Autowired
    @Lazy
    private DynamicRoutingDataSource dynamicRoutingDataSource;

    @Autowired
    private ExecResultExMapper execResultExMapper;


    public int saveResultBatch(List<ExecResult> execResults, String dsName ,String tableName) {
        int result = 0;
        try {
            if(!Default_ResultDataSource.equalsIgnoreCase(dsName)) {
                dstDataSourceService.initDataSource(dsName);
                DynamicDataSourceContextHolder.push(dsName);
                Map<String, DataSource> dynamicDSMap = dynamicRoutingDataSource.getCurrentDataSources();
                if (!ObjectUtils.isEmpty(dynamicDSMap) && dynamicDSMap.containsKey(dsName)) {
                    DataSource ds = dynamicDSMap.get(dsName);
                    if (ds instanceof DruidDataSource) {
                        DruidDataSource druidDataSource = (DruidDataSource) dynamicDSMap.get(dsName);
                        if ("oracle".equals(druidDataSource.getDbType())||"gbase".equalsIgnoreCase(druidDataSource.getDbType())) {
                            return execResultExMapper.replaceBatchByOracle(execResults, tableName);
                        } else if ("dameng".equals(druidDataSource.getDbType())||"dm".equals(druidDataSource.getDbType())) {
                            return execResultExMapper.replaceBatchByDameng(execResults, tableName);
                        } else if ("mysql".equals(druidDataSource.getDbType())) {
                            return execResultExMapper.replaceBatch(execResults, tableName);
                        } else if ("postgresql".equals(druidDataSource.getDbType())) {
                            return execResultExMapper.replaceBatchByPG(execResults, tableName);
                        }

                    }
                }
            }
            Object resultTopic = execResults.get(0).get("resultTopic");
            if (!StringUtils.isEmpty(resultTopic)){
                // 发送mq消息
                defaultMQProducerService.sendRuleResultsMsg(resultTopic.toString(), "save",execResults);
            }
            result = execResultExMapper.replaceBatch(execResults, tableName);
        } catch (Exception ex) {
            log.error("存储规则结果发生异常，详细错误信息：" + ex.getMessage());
        } finally {
            if(!Default_ResultDataSource.equalsIgnoreCase(dsName)) {
                DynamicDataSourceContextHolder.poll();
            }
        }
        return result;
    }

    public int clearResultBatch(List<ExecResult> execResults, String dsName ,String tableName) {
        int result = 0;
        try {
            if(!Default_ResultDataSource.equalsIgnoreCase(dsName)){
                dstDataSourceService.initDataSource(dsName);
                DynamicDataSourceContextHolder.push(dsName);
            }
            Object resultTopic = execResults.get(0).get("resultTopic");
            if (!StringUtils.isEmpty(resultTopic)){
                // 发送mq消息
                defaultMQProducerService.sendRuleResultsMsg(resultTopic.toString(),"delete",execResults);
            }
            result = execResultExMapper.clearBatch(execResults, tableName);
        } catch (Exception ex) {
            log.error("存储规则结果发生异常，详细错误信息：" + ex.getMessage());
        } finally {
            if(!Default_ResultDataSource.equalsIgnoreCase(dsName)) {
                DynamicDataSourceContextHolder.poll();
            }
        }
        return result;
    }

    public int clearResultBatchByIds(List<String> execResultIds, String dsName ,String tableName) {
        int result = 0;
        try {
            if(!Default_ResultDataSource.equalsIgnoreCase(dsName)){
                dstDataSourceService.initDataSource(dsName);
                DynamicDataSourceContextHolder.push(dsName);
            }
            result = execResultExMapper.clearBatchByIds(execResultIds, tableName);
        } catch (Exception ex) {
            log.error("存储构建结果发生异常，详细错误信息：" + ex.getMessage());
        } finally {
            if(!Default_ResultDataSource.equalsIgnoreCase(dsName)) {
                DynamicDataSourceContextHolder.poll();
            }
        }
        return result;
    }


    public List<ExecResult> sumResult(String dsName ,String tableName, List<String> ruleids, List<String> dimfields, List<String> domainsfields, Timestamp from, Timestamp to)
    {
        List<ExecResult> result = null;
        try {
            if(!Default_ResultDataSource.equalsIgnoreCase(dsName)){
                dstDataSourceService.initDataSource(dsName);
                DynamicDataSourceContextHolder.push(dsName);
            }
            result = execResultExMapper.sumResult(tableName,ruleids,dimfields,domainsfields,from,to);
        } catch (Exception ex) {
            result = new ArrayList<>();
            log.error("存储规则结果发生异常，详细错误信息：" + ex.getMessage());
        } finally {
            if(!Default_ResultDataSource.equalsIgnoreCase(dsName)) {
                DynamicDataSourceContextHolder.poll();
            }
        }

        return result;
    }

    public List<ExecResult> avgResult(String dsName ,String tableName, List<String> ruleids, List<String> dimfields, List<String> domainsfields, Timestamp from, Timestamp to)
    {
        List<ExecResult> result = null;
        try {
            if(!Default_ResultDataSource.equalsIgnoreCase(dsName)){
                dstDataSourceService.initDataSource(dsName);
                DynamicDataSourceContextHolder.push(dsName);
            }
            result = execResultExMapper.avgResult(tableName,ruleids,dimfields,domainsfields,from,to);
        } catch (Exception ex) {
            result = new ArrayList<>();
            log.error("存储规则结果发生异常，详细错误信息：" + ex.getMessage());
        } finally {
            if(!Default_ResultDataSource.equalsIgnoreCase(dsName)) {
                DynamicDataSourceContextHolder.poll();
            }
        }

        return result;
    }

    public List<ExecResult> countDistExt1Result(String dsName ,String tableName, List<String> ruleids, List<String> dimfields, List<String> domainsfields, Timestamp from, Timestamp to)
    {
        List<ExecResult> result = null;
        try {
            if(!Default_ResultDataSource.equalsIgnoreCase(dsName)){
                dstDataSourceService.initDataSource(dsName);
                DynamicDataSourceContextHolder.push(dsName);
            }
            result = execResultExMapper.countDistExt1Result(tableName,ruleids,dimfields,domainsfields,from,to);
        } catch (Exception ex) {
            result = new ArrayList<>();
            log.error("存储规则结果发生异常，详细错误信息：" + ex.getMessage());
        } finally {
            if(!Default_ResultDataSource.equalsIgnoreCase(dsName)) {
                DynamicDataSourceContextHolder.poll();
            }
        }

        return result;
    }

    public Page<ExecResult> lookupResult(int current, int size, String dsName, String tableName, String columns,String ruleid, String dimfield, List<String> domainsfields, Timestamp from, Timestamp to) {
        if(StringUtils.isEmpty(columns))
            columns="*";

        List<ExecResult> result = null;
        long total=0;
        try {
            if(!Default_ResultDataSource.equalsIgnoreCase(dsName)){
                dstDataSourceService.initDataSource(dsName);
                DynamicDataSourceContextHolder.push(dsName);
            }
            com.baomidou.mybatisplus.extension.plugins.pagination.Page<ExecResult> pages=execResultExMapper.lookupResult(new com.baomidou.mybatisplus.extension.plugins.pagination.Page(current+1,size), tableName, columns, ruleid, dimfield, domainsfields, from, to);
            result = pages.getRecords();
            total=pages.getTotal();
        } catch (Exception ex) {
            result = new ArrayList<>();
            log.error("存储规则结果发生异常，详细错误信息：" + ex.getMessage());
        } finally {
            if(!Default_ResultDataSource.equalsIgnoreCase(dsName)) {
                DynamicDataSourceContextHolder.poll();
            }
        }


        return new PageImpl<ExecResult>(result, PageRequest.of(current,size), total);
    }

}

