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.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.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.apache.ibatis.session.SqlSessionFactory;
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.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.io.BufferedReader;
import java.io.Reader;
import java.sql.Clob;
import java.sql.Timestamp;
import java.util.*;

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



    @Autowired
    private DstDataSourceExService dstDataSourceService;



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

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

    @Override
    public Integer selectCount(String dsName, EntityModel entityModel, QueryFilter filter) {
        String sql = entityModel.getSqlSegment("COUNT");
        if(StringUtils.isEmpty(dsName))
            dsName = entityModel.getDsName();
        try {
            dstDataSourceService.initDataSource(dsName);
            DynamicDataSourceContextHolder.push(dsName);
            DbEntitySearchContext context = new DbEntitySearchContext();
            context.setFilter(filter);
            QueryWrapper<EntityObj> qw = context.getSelectCond();
            if (!StringUtils.isEmpty(filter.getCustSqlSegment())) {
              qw.and(wrapper -> wrapper.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 dsName, String dataSet, EntityModel entityModel, QueryFilter filter) {

        String sql = entityModel.getSqlSegment(dataSet);
        if (StringUtils.isEmpty(sql))
            return new ArrayList<>();
        else
            return search(dsName, 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 void cursorRead(String dsName, EntityModel entityModel, String sql, QueryFilter filter, LiteDataCallback callback) {
        if(StringUtils.isEmpty(dsName))
            dsName = entityModel.getDsName();
        if(StringUtils.isEmpty(sql))
            sql=entityModel.getSqlSegment("BASE");
        Integer total = this.selectCount(dsName,entityModel, filter);

        callback.total(total);

        //没有业务数据时，不进行后续操作
        if(total == 0){
            return;
        }


        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(dsName);
            DynamicDataSourceContextHolder.push(dsName);
            myMyBatisCursorItemReader.open(new ExecutionContext()); // 开启游标
            //DynamicDataSourceContextHolder.poll();
            List<EntityObj> datas = new ArrayList<>();
            EntityObj rowdata;
            while ((rowdata = (EntityObj) myMyBatisCursorItemReader.read()) != null) {
                datas.add(rowdata);
                if(datas.size() >= processSize){
                    boolean rt=callback.processData(datas);
                    datas.clear();
                    if(!rt)
                        break;
                }
            }
            if(datas.size() > 0){
                callback.processData(datas);
                datas.clear();
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
        } finally {
            try {
                myMyBatisCursorItemReader.close();// 关闭游标
            } catch (Exception ex) {
                //log.error(ex.getMessage());
            }
            DynamicDataSourceContextHolder.poll();
        }
    }

    @Autowired
    @Lazy
    private DynamicRoutingDataSource dynamicRoutingDataSource;

    @Override
    public boolean saveBatch(String dsName, EntityModel entityModel, List<EntityObj> list) {
        try {
            if(StringUtils.isEmpty(dsName))
                dsName=entityModel.getDsName();
            dstDataSourceService.initDataSource(dsName);
            DynamicDataSourceContextHolder.push(dsName);

            String dstype="";
            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);
                    dstype=druidDataSource.getDbType();
                }
            }

            list.forEach(entityObj -> {
                entityObj.keySet().forEach(key -> {
                    Object val=entityObj.get(key);
                    if(!key.toUpperCase().equals(key))
                        entityObj.put(key.toUpperCase(),val);
                    if(val!=null&&val instanceof Long)
                    {
                        FieldModel f=entityModel.getField(key.toUpperCase());
                        if(f!=null&&f.getField()!=null&&f.getField().getDataType()!=null&&f.getField().getDataType().indexOf("TIME")>=0)
                        {
                            entityObj.put(key.toUpperCase(),new Timestamp((Long)val));
                        }
                    }
                });
            });

            int batchsize=500;
            if ("dameng".equals(dstype)||"dm".equals(dstype)) {
                batchsize=100;
                if(list.size()>batchsize)
                {
                    List<List<EntityObj>> splist=LiteDataService.splitList(list, batchsize);
                    splist.forEach(array->{
                        baseMapper.saveBatchDm(entityModel, array);
                    });
                }
                else
                    baseMapper.saveBatchDm(entityModel, list);
            }
            else if ("postgresql".equalsIgnoreCase(dstype))
            {
                batchsize = 100;
                List<FieldModel> fields = entityModel.getFields();
                List<String> clobField = new ArrayList<>();
                for (FieldModel fieldModel : fields) {
                    if(!fieldModel.isPhysicalField())
                        continue;
                    if(fieldModel.getField() != null && fieldModel.getField().getDataType() != null && fieldModel.getField().getDataType().equalsIgnoreCase("CLOB")){
                        clobField.add(fieldModel.getColumnName().toUpperCase());
                    }
                }
                if (clobField.size() > 0){
                    for (EntityObj entityObj : list) {
                        for (String field : clobField) {
                            Object objValue = entityObj.get(field);
                            if (objValue instanceof Clob){
                                String value = ClobToString((Clob) objValue);
                                if (value == null){
                                    entityObj.set(field,objValue);
                                }else {
                                    entityObj.set(field,value);
                                }
                            }
                        }
                    }
                }
                if(list.size()>batchsize)
                {
                    List<List<EntityObj>> splist = LiteDataService.splitList(list, batchsize);
                    splist.forEach(array->{
                        baseMapper.saveBatchPGSql(entityModel, array);
                    });
                }
                else
                {
                    baseMapper.saveBatchPGSql(entityModel, list);
                }
            }
            else
            {
                if(list.size()>batchsize)
                {
                    List<List<EntityObj>> splist=LiteDataService.splitList(list, batchsize);
                    splist.forEach(array->{
                        baseMapper.saveBatch(entityModel, array);
                    });
                }
                else
                    baseMapper.saveBatch(entityModel, list);
            }



            return true;

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

    /**
     * CLOB转String
     * @param clob
     * @return
     */
    public String ClobToString(final Clob clob) {
        if (clob == null) {
            return null;
        }
        StringBuffer sb = new StringBuffer();
        try {
            Reader is = clob.getCharacterStream();
            BufferedReader br = new BufferedReader(is);
            String str = br.readLine(); // 读取第一行
            while (str != null) { // 如果没有到达流的末尾，则继续读取下一行
                sb.append(str);
                str = br.readLine();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return sb.toString();
    }

    @Override
    public boolean save(String dsName, EntityModel entityModel, EntityObj entityObj) {
        try {
            if(StringUtils.isEmpty(dsName))
                dsName=entityModel.getDsName();
            dstDataSourceService.initDataSource(dsName);
            DynamicDataSourceContextHolder.push(dsName);
            entityObj.keySet().forEach(key -> {
                Object val=entityObj.get(key);
                if(!key.toUpperCase().equals(key))
                    entityObj.put(key.toUpperCase(),val);
                if(val!=null&&val instanceof Long)
                {
                    FieldModel f=entityModel.getField(key.toUpperCase());
                    if(f!=null&&f.getField()!=null&&f.getField().getDataType()!=null&&f.getField().getDataType().indexOf("TIME")>=0)
                    {
                        entityObj.put(key.toUpperCase(),new Timestamp((Long)val));
                    }
                }
            });
            return baseMapper.save(entityModel, entityObj);

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


    @Override
    public EntityObj get(String dsName, EntityModel entityModel, EntityObj entityObj) {
        if(StringUtils.isEmpty(dsName))
            dsName=entityModel.getDsName();
        QueryFilter filter=QueryFilter.createQuery();
        entityModel.getKeyFields().forEach(item->{
            Object val=entityObj.get(item.getColumnName());
            if(val!=null)
            {
                filter.eq(item.getColumnName().toUpperCase(),val);
            }
        });
        List<EntityObj> list=this.selectBase(dsName,entityModel,filter);
        if(list.size()==1)
            entityObj.putAll(list.get(0));
        return entityObj;
    }


    @Autowired
    @Lazy
    private SqlSessionFactory sqlSessionFactory;

    @Value("${ibiz.syncBatchSize:500}")
    private int processSize = 500;
    @Override
    public void processList(DataModel dataModel,Timestamp lastModify,LiteDataCallback callback){
        this.processList(dataModel, "", lastModify, callback);
    }
    @Override
    public void processList(DataModel dataModel,String extCond,Timestamp lastModify,LiteDataCallback callback)
    {
        EntityModel entityModel=dataModel.getFactEntityModel();
        FieldModel lastModifyField=entityModel.getLastModifyField();
        QueryFilter filter=new QueryFilter();
        if(lastModifyField!=null)
            filter.ge(lastModifyField.getColumnName(), lastModify);
        if(StringUtils.hasLength(extCond)){
            filter.cust(extCond);
        }

        //记录处理的总业务数据量
        String sql=entityModel.getSqlSegment("CORE");
        Integer total = this.selectCount(entityModel.getDsName(),entityModel, filter);

        callback.total(total);

        //没有业务数据时，不进行后续操作
        if(total == 0){
            return;
        }


        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<EntityObj> qw=context.getSelectCond();
            if(!StringUtils.isEmpty(filter.getCustSqlSegment()))
                qw.and(wrapper -> wrapper.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);
                if(datas.size() >= processSize){
                    boolean rt=callback.processData(datas);
                    datas.clear();
                    if(!rt)
                        break;
                }
            }
            if(datas.size() > 0){
                callback.processData(datas);
                datas.clear();
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
        } finally {
            try {
                myMyBatisCursorItemReader.close();// 关闭游标
            } catch (Exception ex) {
                //log.error(ex.getMessage());
            }
            DynamicDataSourceContextHolder.poll();
        }
    }


}
