package cn.ibizlab.core.lite.extensions.service;

import cn.ibizlab.core.extensions.mapper.DbEntityMapper;
import cn.ibizlab.core.extensions.service.DstDataSourceExService;
import cn.ibizlab.core.extensions.service.RuleEngineExService;
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.ModelObj;
import cn.ibizlab.core.lite.extensions.filter.DbEntitySearchContext;
import cn.ibizlab.core.lite.extensions.model.DataModel;
import cn.ibizlab.core.lite.extensions.model.Property;
import cn.ibizlab.core.lite.service.IMetaModelService;
import cn.ibizlab.core.rule.domain.ExecResult;
import cn.ibizlab.util.filter.QueryFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.io.BufferedReader;
import java.io.Reader;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Service
@Primary
@Slf4j
public class DbEntityService extends ServiceImpl<DbEntityMapper, EntityObj> implements CommonEntityService {

    public static final String Default_ResultTableName = "IBZRULERESULT";

    @Autowired
    private DstDataSourceExService dstDataSourceService;

    @Autowired
    @Lazy
    private DynamicRoutingDataSource dynamicRoutingDataSource;


    @Override
    public List<EntityObj> selectBase(EntityModel entityModel, QueryFilter filter) {
        return search("BASE", entityModel, filter);
    }

    @Override
    public List<EntityObj> selectCore(EntityModel entityModel, QueryFilter filter) {
        return search("CORE", entityModel, filter);
    }

    @Override
    public Integer selectCount(EntityModel entityModel, QueryFilter filter) {
        String sql = entityModel.getSqlSegment("COUNT");
        String dsName = entityModel.getDsName();
        try {
            dstDataSourceService.initDataSource(dsName);
            DynamicDataSourceContextHolder.push(dsName);
            DbEntitySearchContext context = new DbEntitySearchContext();
            context.setFilter(filter);
            QueryWrapper qw = context.getSelectCond();
            if (!StringUtils.isEmpty(filter.getCustSqlSegment()))
                qw.apply(filter.getCustSqlSegment());

            Integer count = baseMapper.searchCount(sql, qw);

            return count;
        } catch (Exception ex) {
            log.error("详细错误信息：" + ex.getMessage() + ", 执行sql：" + sql);
            return null;
        } finally {
            DynamicDataSourceContextHolder.poll();
        }
    }

    @Override
    public List<EntityObj> search(String dataSet, EntityModel entityModel, QueryFilter filter) {

        String sql = entityModel.getSqlSegment(dataSet);
        if (StringUtils.isEmpty(sql))
            return new ArrayList<>();
        else
            return search(entityModel.getDsName(), sql, filter);
    }

    @Override
    public List<EntityObj> search(String dsName, String sql, QueryFilter filter) {
        try {
            dstDataSourceService.initDataSource(dsName);
            DynamicDataSourceContextHolder.push(dsName);
            DbEntitySearchContext context = new DbEntitySearchContext();
            context.setFilter(filter);
            QueryWrapper qw = context.getSelectCond();
            if (!StringUtils.isEmpty(filter.getCustSqlSegment()))
                qw.apply(filter.getCustSqlSegment());

            List<EntityObj> list = baseMapper.search(sql, qw);
            list.forEach(entityObj -> {
                entityObj.keySet().forEach(key -> {

                    Object value = entityObj.get(key);
                    if (value != null && value instanceof java.sql.Clob) {
                        // Clob类型 转String
                        BufferedReader br = null;
                        Reader is = null;
                        try {
                            String reString = "";
                            is = ((java.sql.Clob) value).getCharacterStream();
                            br = new BufferedReader(is);
                            String s = br.readLine();
                            StringBuffer sb = new StringBuffer();
                            while (s != null) {
                                sb.append(s);
                                s = br.readLine();
                            }
                            reString = sb.toString();

                            entityObj.put(key, reString);
                        } catch (Exception ex) {

                        } finally {
                            try {
                                if (br != null) {
                                    br.close();
                                }
                            } catch (Exception ex) {
                            }
                            try {
                                if (is != null) {
                                    is.close();
                                }
                            } catch (Exception ex) {
                            }

                        }

                    }
                });
            });
            return list;
        } catch (Exception ex) {
            log.error("详细错误信息：" + ex.getMessage() + ", 执行sql：" + sql);
            return null;
        } finally {
            DynamicDataSourceContextHolder.poll();
        }
    }

    @Override
    public int saveResultBatch(List<ExecResult> var1) {
        return 0;
    }

    @Override
    public int clearResultBatch(List<ExecResult> var1) {
        return 0;
    }

    @Autowired
    private DbEntityMapper dbEntityMapper;

    public int saveResultBatch(List<ExecResult> execResults, String dsName) {
        int result = 0;
        try {
            if(!ObjectUtils.isEmpty(dsName)){
                dstDataSourceService.initDataSource(dsName);
                DynamicDataSourceContextHolder.push(dsName);
            }
            Map<String, DataSource> dynamicDSMap = dynamicRoutingDataSource.getCurrentDataSources();
            String tableName = getTableName(execResults);
            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())) {
                        return dbEntityMapper.replaceBatchByOracle(execResults, tableName);
                    }
                }
            }
            result = dbEntityMapper.replaceBatch(execResults, tableName);
        } catch (Exception ex) {
            log.error("存储规则结果发生异常，详细错误信息：" + ex.getMessage());
        } finally {
            DynamicDataSourceContextHolder.poll();
        }
        return result;
    }

    /**
     * 获取规则结果表名
     * @param execResults
     * @return
     */
    private String getTableName(List<ExecResult> execResults) {
        String tableName = Default_ResultTableName;
        for(ExecResult execResult : execResults){
            if(!ObjectUtils.isEmpty(execResult.get(RuleEngineExService.Setting_ResultTableName))){
                return String.valueOf(execResult.get(RuleEngineExService.Setting_ResultTableName));
            }
        }
        return tableName;
    }

    public int clearResultBatch(List<ExecResult> execResults, String dsName) {
        int result = 0;
        try {
            String tableName = getTableName(execResults);
            dstDataSourceService.initDataSource(dsName);
            DynamicDataSourceContextHolder.push(dsName);
            result = dbEntityMapper.clearBatch(execResults, tableName);
        } catch (Exception ex) {
            log.error("存储规则结果发生异常，详细错误信息：" + ex.getMessage());
        } finally {
            DynamicDataSourceContextHolder.poll();
        }
        return result;
    }

    @Autowired
    private IMetaModelService metaModelService;

    @Autowired
    private LiteModelService liteModelService;


    @Override
    public List<ModelObj> getModelObjs(String metaModelId, Set<String> fillPropertys, Timestamp lastModify) {
        DataModel dataModel = liteModelService.getDataModel(metaModelId);
        EntityModel entityModel = dataModel.getFactEntityModel();
        FieldModel lastModifyField = entityModel.getLastModifyField();
        QueryFilter filter = new QueryFilter();

        if (lastModifyField != null)
            filter.ge(lastModifyField.getColumnName(), lastModify);

        List<EntityObj> kEntityObjs = this.selectCore(entityModel, filter);

        return getModelObjs(dataModel, fillPropertys, kEntityObjs);
    }

    @Override
    public ModelObj getModelObjs(String metaModelId, Set<String> fillPropertys, Object factKey) {
        DataModel dataModel = liteModelService.getDataModel(metaModelId);
        List<EntityObj> kEntityObjs = new ArrayList<>();
        kEntityObjs.add(new EntityObj().set(dataModel.getFactEntityModel().getKeyField().getColumnName(), factKey));
        List<ModelObj> list = getModelObjs(dataModel, fillPropertys, kEntityObjs);
        if (list.size() > 0)
            return list.get(0);
        return null;
    }

    @Override
    public ModelObj getModelObjs(String metaModelId, Set<String> fillPropertys, Object... factUniKey) {
        DataModel dataModel = liteModelService.getDataModel(metaModelId);
        List<EntityObj> kEntityObjs = new ArrayList<>();
        EntityObj entityObj = new EntityObj();
        List<FieldModel> uniKeys = dataModel.getFactEntityModel().getUnionKeyFields();
        for (int i = 0; i < uniKeys.size(); i++)
            entityObj.set(uniKeys.get(i).getColumnName(), factUniKey[i]);
        kEntityObjs.add(entityObj);
        List<ModelObj> list = getModelObjs(dataModel, fillPropertys, kEntityObjs);
        if (list.size() > 0)
            return list.get(0);
        return null;
    }

    @Override
    public List<ModelObj> getModelObjs(String metaModelId, Set<String> fillPropertys, List<EntityObj> kEntityObjs) {
        DataModel dataModel = liteModelService.getDataModel(metaModelId);
        return getModelObjs(dataModel, fillPropertys, kEntityObjs);
    }

    public List<ModelObj> getModelObjs(DataModel dataModel, Set<String> fillPropertys, List<EntityObj> kEntityObjs) {
        EntityModel entityModel = dataModel.getFactEntityModel();
        List<ModelObj> rt = new ArrayList<>();
        List<EntityObj> factEntityList = new ArrayList<>();
        if (fillPropertys == null || fillPropertys.size() == 0 || fillPropertys.contains(dataModel.getFactPorperty().getPropertyName()))
            splitList(kEntityObjs, 1000).forEach(list -> factEntityList.addAll(this.selectBase(entityModel, QueryFilter.createQuery().cust(dataModel.lookup(list)))));
        factEntityList.forEach(entityObj ->
                rt.add(new ModelObj().setDataModel(dataModel).setFactEntity(entityObj.setProperty(dataModel.getFactPorperty()))));
        fillEntityObj(dataModel, fillPropertys, rt);
        dataModel.getNestedDataModels().forEach(subModel ->
                this.getModelObjs(subModel, fillPropertys, factEntityList).forEach(subObj -> subObj.findParent(rt)));
        rt.forEach(modelObj -> modelObj.getTimestamp());
        return rt;
    }

    public void fillEntityObj(DataModel dataModel, Set<String> fillPropertys, List<ModelObj> modelObjs) {
        if (dataModel.getObjectProperties().size() > 1) {
            for (Property property : dataModel.getObjectProperties()) {
                if (property.getPropertyName().equals(dataModel.getFactPorperty().getPropertyName()))
                    continue;
                EntityModel entityModel = property.getEntityModel();
                List<EntityObj> entityObjs = new ArrayList<>();
                if (fillPropertys == null || fillPropertys.size() == 0 || fillPropertys.contains(property.getPropertyName()))
                    splitList(modelObjs, 1000).forEach(list -> entityObjs.addAll(this.selectBase(entityModel, QueryFilter.createQuery().cust(property.lookup(list)))));

                entityObjs.forEach(entityObj -> {
                    entityObj.setProperty(property);
                    entityObj.findModel(modelObjs);
                });
            }
        }
    }


    private <T> List<List<T>> splitList(List<T> list, int groupSize) {
        int length = list.size();
        // 计算可以分成多少组
        int num = (length + groupSize - 1) / groupSize; // TODO
        List<List<T>> newList = new ArrayList<>(num);
        for (int i = 0; i < num; i++) {
            // 开始位置
            int fromIndex = i * groupSize;
            // 结束位置
            int toIndex = (i + 1) * groupSize < length ? (i + 1) * groupSize : length;
            newList.add(list.subList(fromIndex, toIndex));
        }
        return newList;
    }


}
