package cn.ibizlab.core.extensions.service;

import cn.ibizlab.core.extensions.domain.EngineMQMsg;
import cn.ibizlab.core.extensions.util.MQConsumeMsgListenerProcessor;
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.service.LiteDataCallback;
import cn.ibizlab.core.lite.extensions.service.LiteDataService;
import cn.ibizlab.core.lite.extensions.service.LiteModelService;
import cn.ibizlab.core.lite.mapper.TableSyncMapper;
import cn.ibizlab.core.lite.service.impl.TableSyncServiceImpl;
import cn.ibizlab.core.rule.domain.ExecLog;
import cn.ibizlab.core.rule.service.IExecLogService;
import cn.ibizlab.util.filter.QueryFilter;
import cn.ibizlab.util.helper.DataObject;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import cn.ibizlab.core.lite.domain.TableSync;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.context.annotation.Primary;
import org.springframework.util.StringUtils;

import java.sql.Timestamp;
import java.sql.Wrapper;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 实体[ETL表同步] 自定义服务对象
 */
@Slf4j
@Primary
@Service("TableSyncExService")
public class TableSyncExService extends TableSyncServiceImpl {

    @Value("${ibiz.isDistinct:false}")
    private boolean isDistinct;

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

    /**
     * [Sync:Sync] 行为扩展
     * @param et
     * @return
     */
    @Override
    public TableSync sync(TableSync et) {
        List<TableSync> list = null;
        String params= DataObject.getStringValue(et.get("param"),"");
        LambdaQueryWrapper<TableSync> wrapper= Wrappers.<TableSync>lambdaQuery();
        if(!StringUtils.isEmpty(params))
            wrapper.like(TableSync::getGroup,params);
        if(!StringUtils.isEmpty(et.getId()))
            wrapper.eq(TableSync::getId,et.getId());
        wrapper.eq(TableSync::getIsEnable,1);
        list=this.list(wrapper);
        sync(list);
        return super.sync(et);
    }

    @Autowired
    private LiteModelService liteModelService;


    @Autowired
    private LiteDataService liteDataService;

    @Autowired
    private IExecLogService execLogService;

    public void sync(List<TableSync> list)
    {
        for(TableSync sync:list)
        {
            try
            {
                if(sync.getIsEnable()!=null&&sync.getIsEnable()==0)
                    continue;
                String sourceSystemId=sync.getSourceSystemId();
                String sourceDsId=sync.getSourceDsId();
                String sourceEntityName=sync.getSourceEntityName();
                String systemId=sync.getSystemId();
                String dsId=sync.getDsId();
                String entityName=sync.getEntityName();
                if(StringUtils.isEmpty(entityName)||StringUtils.isEmpty(systemId))
                    continue;
                if(StringUtils.isEmpty(sourceEntityName))
                    sourceEntityName=entityName;
                if(StringUtils.isEmpty(sourceSystemId))
                    sourceSystemId=systemId;
                EntityModel entityModel=liteModelService.getEntityModel(systemId,entityName);
                if(StringUtils.isEmpty(dsId))
                    dsId=entityModel.getDsName();
                EntityModel sourceModel=liteModelService.getEntityModel(sourceSystemId,sourceEntityName);
                if(StringUtils.isEmpty(sourceDsId))
                    sourceDsId=sourceModel.getDsName();
                if(sourceDsId.equals(dsId)&&sourceEntityName.equals(entityName))
                    continue;


                Timestamp lastTimestamp=DataObject.getTimestampValue(sync.getEtlTimestamp(),DataObject.getBeginDate());


                {
                    Calendar c = Calendar.getInstance();
                    c.setTime(lastTimestamp);
                    c.add(Calendar.HOUR, -8);
                    lastTimestamp=new java.sql.Timestamp(c.getTime().getTime());
                }




                QueryFilter filter=QueryFilter.createQuery();
                if(sourceModel.getLastModifyField()!=null)
                    filter.ge(sourceModel.getLastModifyField().getColumnName(),lastTimestamp);
                Timestamp runTimestamp=new Timestamp(System.currentTimeMillis());

                final String dsName=dsId;

                ExecLog execlog = new ExecLog();
                execlog.setName("[SYNC]" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "[" + entityName + "]");
                execlog.setKeyValueField(entityName);
                execlog.setRetCode(0);
                execlog.setSucc(0);
                execlog.setSystemId(systemId);
                execlog.setRunResult("Service成功，process successfully");
                liteDataService.searchCursor(sourceModel,sync.getSourceExpression(),sourceDsId,filter, new LiteDataCallback<List<EntityObj>>() {
                    @Override
                    public void total(Integer total) {

                        if(total > 0)
                        {
                            String updateSql = "UPDATE ibztablesync SET lastread=#{et.lastread},lastwrite=0," +
                                    "lastruntime=#{et.lastruntime}," +
                                    "lastrunresult='RUNNING' WHERE syncid = #{et.syncid}";
                            HashMap<String, Object> param = new HashMap<>();
                            param.put("lastread", total);
                            param.put("lastruntime", runTimestamp);
                            param.put("syncid", sync.getId());
                            updateBySQL(updateSql,param);

                            execlog.setCnt(total);
                            execLogService.create(execlog);
                        }
                        else {
                            String updateSql = "UPDATE ibztablesync SET lastread=0,lastwrite=0," +
                                    "lastruntime=#{et.lastruntime}," +
                                    "lastendtime=#{et.lastendtime}," +
                                    "etltimestamp=#{et.etltimestamp}," +
                                    "lastrunresult='FINISH' WHERE syncid = #{et.syncid}";
                            HashMap<String, Object> param = new HashMap<>();
                            param.put("lastendtime", new Timestamp(System.currentTimeMillis()));
                            param.put("lastruntime", runTimestamp);
                            param.put("etltimestamp", runTimestamp);
                            param.put("syncid", sync.getId());
                            updateBySQL(updateSql,param);
                        }
                    }

                    @Override
                    public boolean processData(List<EntityObj> datas) {
                        int beforeSize = datas.size();
                        boolean flag = false;
                        if (isDistinct){
                            // 重复数据集合
                            List<EntityObj> datas2 = new ArrayList<>();
                            ArrayList<String> arrayList = new ArrayList<>();
                            LinkedHashMap<Object,EntityObj> params = new LinkedHashMap<>();
                            List<FieldModel> keyFields = entityModel.getKeyFields();
                            for (EntityObj entityObj: datas) {
                                Object strKey = null;
                                if (keyFields != null && keyFields.size() > 0){
                                    for (FieldModel fieldModel : keyFields){
                                        Object value = entityObj.get(fieldModel.getColumnName());
                                        if (StringUtils.isEmpty(strKey)){
                                            strKey = value ;
                                        }else {
                                            strKey = strKey + "||" + value;
                                        }
                                    }
                                }
                                if (!StringUtils.isEmpty(strKey)){
                                    if (params.containsKey(strKey)){
                                        try{
                                            arrayList.add(JSONObject.toJSONString(entityObj));
                                        }catch (Exception e){
                                            e.printStackTrace();
                                        }
                                    }
                                    params.put(strKey,entityObj);
                                }
                            }
                            for (EntityObj entityObj : params.values()) {
                                datas2.add(entityObj);
                            }
                            int afterSize = datas2.size();
                            if (afterSize != beforeSize){
                                try{
                                    flag = true;
                                    String beforeData = JSONObject.toJSONString(datas);
                                    String afterData = JSONObject.toJSONString(datas2);
                                    log.trace(String.format("--ETL表同步--表名称：%1$s ,去重前数据量：%2$s，去重后数据量：%3$s，\r\n 去重前数据内容：%4$s，\r\n 去重后数据内容：%5$s，\r\n 重复数据：%6$s",entityModel.getTableName(),beforeSize,afterSize,beforeData,afterData,arrayList));
                                }catch (Exception e){
                                    e.printStackTrace();
                                }
                            }
                            if (afterSize > 0){
                                datas = datas2;
                            }
                        }
                        boolean rt=liteDataService.saveBatch(entityModel,dsName,datas);

                        if(rt)
                        {
                            String updateSql = "UPDATE ibztablesync SET lastwrite=(CASE WHEN lastwrite is null then 0 else lastwrite end+#{et.count}) WHERE syncid = #{et.syncid}";
                            HashMap<String, Object> param = new HashMap<>();
                            param.put("count", beforeSize);
                            param.put("syncid", sync.getId());
                            updateBySQL(updateSql,param);

                            updateSql = "UPDATE ibztablesync SET "+
                                    "lastendtime=#{et.lastendtime}," +
                                    "etltimestamp=#{et.etltimestamp}," +
                                    "lastrunresult='FINISH' WHERE syncid = #{et.syncid} and lastread <= lastwrite ";
                            param = new HashMap<>();
                            param.put("lastendtime", new Timestamp(System.currentTimeMillis()));
                            param.put("etltimestamp", runTimestamp);
                            param.put("syncid", sync.getId());
                            updateBySQL(updateSql,param);

                            try {
                                DynamicDataSourceContextHolder.push("master");
                                execlog.setSucc(execlog.getSucc() + datas.size());
                                if (flag){
                                    // 去重后，写入数据库成功
                                    execlog.setRetCode(5);
                                }
                                execLogService.update(execlog);
                            } catch (Exception ex) {
                                log.error("详细错误信息：" + ex.getMessage());
                            } finally {
                                DynamicDataSourceContextHolder.poll();
                            }
                        }
                        else
                        {
                            String updateSql = "UPDATE ibztablesync SET lastwrite=CASE WHEN lastwrite is null then 0 else lastwrite end," +
                                    "lastruntime=#{et.lastruntime}," +
                                    "lastendtime=#{et.lastendtime}," +
                                    //"etltimestamp=#{et.etltimestamp}," +
                                    "lastrunresult='ERROR' WHERE syncid = #{et.syncid}";
                            HashMap<String, Object> param = new HashMap<>();
                            param.put("lastendtime", new Timestamp(System.currentTimeMillis()));
                            param.put("lastruntime", runTimestamp);
                            //param.put("etltimestamp", runTimestamp);
                            param.put("syncid", sync.getId());
                            updateBySQL(updateSql,param);

                            try {
                                DynamicDataSourceContextHolder.push("master");
                                String runBody = JSONObject.toJSONString(datas);
                                execlog.setRunBody(runBody);
                                execlog.setRetCode(execlog.getRetCode() + 1);
                                execlog.setRunResult("sync同步失败：" + sync.getEntityName());
                                execLogService.update(execlog);
                            } catch (Exception ex) {
                                log.error("详细错误信息：" + ex.getMessage());
                            } finally {
                                DynamicDataSourceContextHolder.poll();
                            }
                        }

                        return rt;
                    }
                });
            }
            catch (Exception ex)
            {
                log.error("sync同步失败:"+sync.getEntityName()+":"+ex.getMessage());
                ex.printStackTrace();
            }
        }
    }

    private void updateBySQL(String updateSql , Map param)
    {
        try {
            DynamicDataSourceContextHolder.push("master");
            this.getBaseMapper().updateBySQL(updateSql, param);
        } catch (Exception ex) {
            log.error("详细错误信息：" + ex.getMessage() );
        } finally {
            DynamicDataSourceContextHolder.poll();
        }
    }

}

