package com.persagy.proxy.migration.handler;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.persagy.dmp.basic.model.QueryCriteria;
import com.persagy.dmp.basic.utils.JsonNodeUtils;
import com.persagy.dmp.common.constant.ResponseCode;
import com.persagy.dmp.common.model.entity.BaseEntity;
import com.persagy.dmp.define.client.DigitalObjectInfoFacade;
import com.persagy.dmp.define.entity.ObjectInfoDefine;
import com.persagy.dmp.rwd.migrate.entity.SyncData;
import com.persagy.proxy.adm.request.AdmResponse;
import com.persagy.proxy.common.entity.InstanceUrlParam;
import com.persagy.proxy.migration.constant.MigrationConstant;
import com.persagy.proxy.migration.constant.MigrationTable;
import com.persagy.proxy.migration.constant.MigrationType;
import com.persagy.proxy.migration.model.DataMigrationExcel;
import com.persagy.proxy.migration.model.DataMigrationResponse;
import com.persagy.proxy.migration.model.MigrationInfo;
import com.persagy.proxy.migration.service.Impl.MigrationAbstractServiceImpl;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lvxianyun
 * @title: DefineInfoMigration
 * @projectName adm-middleware
 * @description:  迁移adm交付的数据到现场，覆盖现场数据，提供增加、删除、修改 记录放入excel; DefineInfo 数据迁移 adm -> target project
 * @date 2021/12/8 17:28
 */
@Slf4j
public class DefineInfoMigration extends MigrationAbstractServiceImpl<ObjectInfoDefine> implements IDataMigration, IDefineDataMigration<ObjectInfoDefine>, IDataMigrationProcess<ObjectInfoDefine> {
    
    private static DefineInfoMigration instance;

    private DefineInfoMigration() {}

    // 静态方法返回该实例
    public static DefineInfoMigration getInstance() {
        if(instance == null) {
            synchronized (DefineInfoMigration.class) {
                if (instance == null) {
                    instance = new DefineInfoMigration();
                }
            }
        }
        return instance;
    }

    /**
     * 数据迁移
     *
     * @param context
     * @param migrationInfo
     * @return
     */
    @Override
    public List<DataMigrationExcel> migrateForExcel(InstanceUrlParam context, MigrationInfo migrationInfo) {
        log.info("######################### ObjectInfoDefine 数据迁移内容获取开始 #########################");
        QueryCriteria queryCriteria = getCustomQueryCriteria(context);
        List<ObjectInfoDefine> admDefineList = getAdmData(context, queryCriteria);
        String queryUrl = requestUrl(context, migrationInfo, MigrationType.QUERY.getCode());
        List<ObjectInfoDefine> projectDefineList = getProjectData(queryCriteria, ObjectInfoDefine.class, queryUrl);
        List<DataMigrationExcel> dataMigrationExcels = compareByData(admDefineList, projectDefineList);
        log.info("######################### ObjectInfoDefine 数据迁移内容获取结束 #########################");
        return dataMigrationExcels;
    }

    @Override
    public AdmResponse migrateForLog(InstanceUrlParam context, MigrationInfo migrationInfo) {
        log.info("######################### DefineInfo 同步开始 #########################");
        long start = System.currentTimeMillis();
        List<SyncData> syncDataList = new ArrayList<>();

        QueryCriteria queryCriteria = getCustomQueryCriteria(context);

        List<ObjectInfoDefine> admDefineList = getAdmData(context, queryCriteria);
        String queryUrl = requestUrl(context, migrationInfo, MigrationType.QUERY.getCode());
        List<ObjectInfoDefine> projectDefineList = getProjectData(queryCriteria, ObjectInfoDefine.class, queryUrl);

        if(CollUtil.isEmpty(admDefineList)){
            log.error(" ######################### DefineInfo adm已交付需要同步的数据:"+ ResponseCode.C0320.getDesc() +" 同步结束 #########################");
        }
        String insertUrl = requestUrl(context, migrationInfo, MigrationType.CREATE.getCode());
        if(CollUtil.isEmpty(projectDefineList)){
            log.info("######################### DefineInfo 批量插入数据 #########################");
            DataMigrationResponse response = insertBatch(admDefineList, ObjectInfoDefine.class, insertUrl);
            syncDataList.addAll(processDataForLog(response, MigrationType.CREATE.getCode()));
        }
        List<ObjectNode> objectNodeListPro = JsonNodeUtils.toListNode(projectDefineList, null, null);
        Map<String,Object> projectDefineMap = toEntityMap(objectNodeListPro, ObjectInfoDefine.class);
        List<ObjectNode> objectNodeListAdm = JsonNodeUtils.toListNode(admDefineList, null, null);
        Map<String,Object> admDefineMap = toEntityMap(objectNodeListAdm, ObjectInfoDefine.class);


        //差集 新增
        log.info("######################### DefineInfo 批量插入数据 #########################");
        Map<String,Object> subtractFromDel = doSubtractAdd(admDefineMap, projectDefineMap);
        if(!CollUtil.isEmpty(subtractFromDel)){
            List<ObjectInfoDefine> insertData = toList(subtractFromDel, admDefineList);
            syncDataList.addAll(processDataForLog(insertBatch(insertData, ObjectInfoDefine.class, insertUrl), MigrationType.CREATE.getCode()));
        }

        //差集 删除
        log.info("######################### DefineInfo 批量处理删除数据 #########################");
        Map<String,Object> subtractFromDelete = doSubtractDel(admDefineMap, projectDefineMap);
        if(!CollUtil.isEmpty(subtractFromDelete)){
            List<String> deleteIds = subtractFromDelete.entrySet().stream().map(en -> en.getKey()).collect(Collectors.toList());
            String delUrl = requestUrl(context, migrationInfo, MigrationType.DELETE.getCode());
            //处理删除的数据
            DataMigrationResponse dataMigrationResponse = deleteBatch(deleteIds, ObjectInfoDefine.class, delUrl);
            List<String> successIds = (List<String>) dataMigrationResponse.getData();
            List<ObjectInfoDefine> delObjs = toListByIds(successIds, projectDefineList);
            dataMigrationResponse.setData(delObjs);
            syncDataList.addAll(processDataForLog(dataMigrationResponse, MigrationType.DELETE.getCode()));
        }

        //交集更新
        log.info("######################### DefineInfo 批量处理更新数据 #########################");
        Map<String,Object> intersectionUpdateFrom = doIntersectionGetFrom(admDefineMap, projectDefineMap);
        Map<String,Object> intersectionUpdateTo = doIntersectionGetTo(admDefineMap, projectDefineMap);

        if(!CollUtil.isEmpty(intersectionUpdateFrom) && !CollUtil.isEmpty(intersectionUpdateTo)){
            Map<String, Object> compareData = compareData(intersectionUpdateFrom, intersectionUpdateTo);
            if(CollUtil.isNotEmpty(compareData) && compareData.size() > 0){
                List<ObjectInfoDefine> updateData = toList(compareData, admDefineList);
                String updateUrl = requestUrl(context, migrationInfo, MigrationType.UPDATE.getCode());
                syncDataList.addAll(processDataForLog(updateBatch(updateData, ObjectInfoDefine.class, updateUrl), MigrationType.UPDATE.getCode()));
            }
        }
        long end = System.currentTimeMillis();
        log.info("######################### DefineInfo 数据迁移已结束 时间："+(end-start)+" 共计："+syncDataList.size()+"条 #########################");
        super.addSynLog(context, syncDataList);
        return AdmResponse.success();
    }

    /**
     * 获取adm交付的数据，默认获取有效数据
     *
     * @param context
     * @return
     */
    @Override
    public List<ObjectInfoDefine> getAdmData(InstanceUrlParam context, QueryCriteria queryCriteria) {
        log.info("######################### DefineInfo 获取adm交付数据 #########################");
        List<ObjectInfoDefine> infos = DigitalObjectInfoFacade.query(context.getGroupCode(),context.getProjectId(),context.getUserId(),context.getUserId(),queryCriteria);
        return infos;
    }

    /**
     * 根据map转为需要处理的对象集合
     * 使用 MigrationConstant.spliter 作为拼接符
     * @param fromMap
     * @param fromList
     * @return
     */

    public List<ObjectInfoDefine> toList(Map<String, Object> fromMap, List<ObjectInfoDefine> fromList) {
        if(CollUtil.isEmpty(fromMap) || CollUtil.isEmpty(fromList)){
            return Collections.emptyList();
        }
        return toListByIds(fromMap.keySet(), fromList);
    }

    public List<ObjectInfoDefine> toListByIds(Collection<String> ids, List<ObjectInfoDefine> fromList) {
        if(CollUtil.isEmpty(ids) || CollUtil.isEmpty(fromList)){
            return Collections.emptyList();
        }
        List<ObjectInfoDefine> results = new ArrayList<>(ids.size());
        Map<String, ObjectInfoDefine> objectInfoDefineMap = CollUtil.fieldValueMap(fromList, BaseEntity.PROP_ID);
        for(String key : ids){
            ObjectInfoDefine define = objectInfoDefineMap.get(key);
            if (define!= null) {
                results.add(define);
            }

        }
        return results;
    }


    /**
     * 拼接访问链接
     *
     * @param context
     * @param migrationInfo
     * @param type
     * @return
     */
    @Override
    public String requestUrl(InstanceUrlParam context, MigrationInfo migrationInfo, String type) {
        if(StrUtil.isBlank(migrationInfo.getTargetUrl())){
            return null;
        }
        StringBuilder url = new StringBuilder(migrationInfo.getTargetUrl());
        if(type.equals(MigrationType.QUERY.getCode())) {
            url.append(MigrationConstant.DEF_FUNCID);
        }else if(type.equals(MigrationType.CREATE.getCode())){
            url.append(MigrationConstant.DEF_FUNCID_INSERT);
        }else if(type.equals(MigrationType.UPDATE.getCode())){
            url.append(MigrationConstant.DEF_FUNCID_UPDATE);
        }else{
            url.append(MigrationConstant.DEF_FUNCID_DELETE);
        }
        return commonUrl(context, url.toString(), migrationInfo);
    }

    /**
     * 将目标集合处理成excel格式的集合  使用与insert/delete
     *
     * @param list
     * @return
     */
    @Override
    public List<DataMigrationExcel> processDataForExcel(Collection list, String type) {
        if(CollUtil.isEmpty(list)){
            return Collections.emptyList();
        }
        List<DataMigrationExcel> dataMigrationExcels = new ArrayList<>();
        for(Object obj : list){
            ObjectInfoDefine define = (ObjectInfoDefine) obj;
            DataMigrationExcel excel = new DataMigrationExcel();
            excel.setId(define.getId());
            excel.setCode(define.getCode());
            excel.setName(define.getName());
            excel.setType(type);
            excel.setCreateTime(define.getCreationTime().toString());
            excel.setUpdateTime(define.getModifiedTime() == null ? "" : define.getModifiedTime().toString());
            dataMigrationExcels.add(excel);
        }
        return dataMigrationExcels;
    }

    /**
     * 将目标集合处理成SyncData(日志)格式的集合  入库
     *
     * @param dataMigrationResponse
     * @param type                  insert/delete
     * @return
     */
    @Override
    public List<SyncData> processDataForLog(DataMigrationResponse dataMigrationResponse, String type) {
        if(CollUtil.isEmpty(dataMigrationResponse.getData()) && CollUtil.isEmpty(dataMigrationResponse.getErrorMsg())){
            return Collections.emptyList();
        }
        List<SyncData> syncDataList = new ArrayList<>();
        if(CollUtil.isNotEmpty(dataMigrationResponse.getData())){
            for(Object obj : dataMigrationResponse.getData()){
                ObjectInfoDefine infoDefine = (ObjectInfoDefine) obj;
                SyncData syncData = new SyncData();
                syncData.setTableName(MigrationTable.DTDEFINEINFO.getCode());
                syncData.setTargetId(infoDefine.getId());
                syncData.setName(infoDefine.getName());
                syncData.setCode(infoDefine.getCode());
                syncData.setClassCode(infoDefine.getClassCode());
                syncData.setType(type);//操作
                syncDataList.add(syncData);
            }
        }
        if(CollUtil.isNotEmpty(dataMigrationResponse.getErrorMsg())){
            syncDataList.addAll(addErrorInfoForLog(dataMigrationResponse.getErrorMsg(),MigrationTable.DTDEFINEINFO.getCode()));
        }
        return syncDataList;
    }

    /**
     * 获取标准字典数量，默认获取有效数据
     *
     * @param context
     * @param queryCriteria
     * @return count
     */
    @Override
    public Long getAdmCountData(InstanceUrlParam context, QueryCriteria queryCriteria) {
        Long count = DigitalObjectInfoFacade.queryForCount(context.getGroupCode(),context.getProjectId(),context.getUserId(),context.getUserId(),queryCriteria);
        return count;
    }

    /**
     * 标准字典 根据整体数量进行比较
     *
     * @param context
     * @param migrationInfo
     * @return AdmResponse
     */
    @Override
    public AdmResponse compareByCount(InstanceUrlParam context, MigrationInfo migrationInfo) {
        QueryCriteria queryCriteria = getCriterionQueryCriteria();
        queryCriteria.setOnlyCount(true);
        long admCont = getAdmCountData(context, queryCriteria);

        String queryUrl = requestUrl(context, migrationInfo, MigrationType.QUERY.getCode());
        long proCount = queryCountByHttp(queryUrl, queryCriteria);
        //是否需要更新字典
        boolean needUpdateDic = true;
        List<DataMigrationExcel> dataMigrationExcels = new ArrayList<>();
        if(admCont - proCount == 0){
            //TODO 对比标准字典数据
           /* dataMigrationExcels = compareCriterionByData(context, migrationInfo, admCont);
            if(CollUtil.isEmpty(dataMigrationExcels)){
                needUpdateDic = false;
            }*/
            needUpdateDic = false;
        }
        //验证失败 需要更新字典
        if(needUpdateDic){
            String msg = "交付数据标准字典与项目现场标准字典不匹配（数量不一致），请联系管理者确认是否需要字典升级";
            AdmResponse admResponse = AdmResponse.failure(msg);
            DataMigrationExcel dataMigrationExcel = new DataMigrationExcel();
            dataMigrationExcel.setId("error");
            dataMigrationExcel.setName(msg);
            dataMigrationExcels.add(dataMigrationExcel);
            admResponse.setContent(dataMigrationExcels);
             return admResponse;
        }else{
            return AdmResponse.success();
        }
    }

    /**
     * 标准字典 根据整体数据进行逐条对比
     *
     * @param context
     * @param migrationInfo
     * @return AdmResponse
     */
    @Override
    public List<DataMigrationExcel> compareCriterionByData(InstanceUrlParam context, MigrationInfo migrationInfo) {
        log.info("######################### ObjectInfoDefine 数据迁移 标准字典验证 开始 #########################");
        QueryCriteria queryCriteria = getCriterionQueryCriteria();
        List<ObjectInfoDefine> admDefineList = getAdmData(context, queryCriteria);
        String queryUrl = requestUrl(context, migrationInfo, MigrationType.QUERY.getCode());
        List<ObjectInfoDefine> projectDefineList = getProjectData(queryCriteria, ObjectInfoDefine.class, queryUrl);
        List<DataMigrationExcel> dataMigrationExcels = compareByData(admDefineList, projectDefineList);
        log.info("######################### ObjectInfoDefine 数据迁移 标准字典验证 结束 #########################");
        return dataMigrationExcels;
    }

    public List<DataMigrationExcel> compareCriterionByData(InstanceUrlParam context, MigrationInfo migrationInfo, long totalCount) {
        log.info("######################### ObjectInfoDefine 数据迁移 标准字典验证 开始 #########################");
        QueryCriteria queryCriteria = getCriterionQueryCriteria();
        queryCriteria.setSize(10000L);
        long pages = 1L;
        if(totalCount > 10000){
            pages = totalCount / 10000 + 1;
        }
        List<DataMigrationExcel> dataMigrationExcels = new ArrayList<>();
        for(long page = 1; page < pages; page++){
            queryCriteria.setPage(page);
            List<ObjectInfoDefine> admDefineList = getAdmData(context, queryCriteria);
            String queryUrl = requestUrl(context, migrationInfo, MigrationType.QUERY.getCode());
            List<ObjectInfoDefine> projectDefineList = getProjectData(queryCriteria, ObjectInfoDefine.class, queryUrl);
            dataMigrationExcels.addAll(compareByData(admDefineList, projectDefineList));
        }

        log.info("######################### ObjectInfoDefine 数据迁移 标准字典验证 结束 #########################");
        return dataMigrationExcels;
    }

    @Override
    public List<DataMigrationExcel> compareByData(List<ObjectInfoDefine> admDefineList, List<ObjectInfoDefine> projectDefineList) {
        log.info("######################### DefineInfo 数据迁移内容获取开始 #########################");
        long start = System.currentTimeMillis();
        List<DataMigrationExcel> dataMigrationExcels = new ArrayList<>();

        if(CollUtil.isEmpty(admDefineList)){
            log.error(" ######################### DefineInfo adm已交付需要同步的数据:"+ ResponseCode.C0320.getDesc() +" 同步结束 #########################");
            return dataMigrationExcels;
        }
        //String insertUrl = requestUrl(context, migrationInfo, MigrationType.INSERT.getCode());
        if(CollUtil.isEmpty(projectDefineList)){
            log.info("######################### DefineInfo 获取批量插入数据 #########################");
            //DataMigrationResponse response = insertBatch(admDefineList, ObjectInfoDefine.class, insertUrl);
            dataMigrationExcels.addAll(processDataForExcel(admDefineList, MigrationType.CREATE.getCode()));
            return dataMigrationExcels;
        }
        List<ObjectNode> objectNodeListPro = JsonNodeUtils.toListNode(projectDefineList, null, null);
        Map<String,Object> projectDefineMap = toEntityMap(objectNodeListPro, ObjectInfoDefine.class);
        List<ObjectNode> objectNodeListAdm = JsonNodeUtils.toListNode(admDefineList, null, null);
        Map<String,Object> admDefineMap = toEntityMap(objectNodeListAdm, ObjectInfoDefine.class);


        //差集 新增
        log.info("######################### DefineInfo 获取批量插入数据 #########################");
        Map<String,Object> subtractFromDel = doSubtractAdd(admDefineMap, projectDefineMap);
        if(!CollUtil.isEmpty(subtractFromDel)){
            List<ObjectInfoDefine> insertData = toList(subtractFromDel, admDefineList);
            dataMigrationExcels.addAll(processDataForExcel(insertData, MigrationType.CREATE.getCode()));
        }

        //差集 删除
        log.info("######################### DefineInfo 获取批量处理删除数据 #########################");
        Map<String,Object> subtractFromDelete = doSubtractDel(admDefineMap, projectDefineMap);
        if(!CollUtil.isEmpty(subtractFromDelete)){
            List<String> deleteIds = subtractFromDelete.entrySet().stream().map(en -> en.getKey()).collect(Collectors.toList());
            //String delUrl = requestUrl(context, migrationInfo, MigrationType.DELETE.getCode());
            //DataMigrationResponse response = deleteBatch(deleteIds, ObjectInfoDefine.class, delUrl);
            List<ObjectInfoDefine> delData = toListByIds(deleteIds, projectDefineList);
            dataMigrationExcels.addAll(processDataForExcel(delData, MigrationType.DELETE.getCode()));
        }

        //交集更新
        log.info("######################### DefineInfo 获取批量处理更新数据 #########################");
        Map<String,Object> intersectionUpdateFrom = doIntersectionGetFrom(admDefineMap, projectDefineMap);
        Map<String,Object> intersectionUpdateTo = doIntersectionGetTo(admDefineMap, projectDefineMap);

        if(!CollUtil.isEmpty(intersectionUpdateFrom) && !CollUtil.isEmpty(intersectionUpdateTo)){
            //TODO 重写比较方法，dataSource 数据顺序不一致，导致对比失败，目前先跳过
            Map<String, Object> compareData = compareData(intersectionUpdateFrom, intersectionUpdateTo);
            if(CollUtil.isNotEmpty(compareData) && compareData.size() > 0){
                List<ObjectInfoDefine> updateData = toList(compareData, admDefineList);
                //String updateUrl = requestUrl(context, migrationInfo, MigrationType.UPDATE.getCode());
                //updateBatch(updateData, ObjectInfoDefine.class, updateUrl)
                dataMigrationExcels.addAll(processDataForExcel(updateData, MigrationType.UPDATE.getCode()));
            }
        }
        long end = System.currentTimeMillis();
        log.info("######################### DefineInfo 数据迁移内容获取已结束 时间："+(end-start)+" #########################");
        return dataMigrationExcels;
    }

    /**
     * 项目级、集团级定义
     * @return
     */
    private QueryCriteria getCustomQueryCriteria(InstanceUrlParam context){
        QueryCriteria queryCriteria = new QueryCriteria();
        ObjectNode criteria = JsonNodeFactory.instance.objectNode();
        criteria.put("valid", 1);

        ObjectNode andOr = JsonNodeFactory.instance.objectNode();
        andOr.put("groupCode", context.getGroupCode());
        andOr.put("projectId", context.getProjectId());

        criteria.putPOJO("$andOr", andOr);

        queryCriteria.setCriteria(criteria);
        queryCriteria.setSize(-1L);
        return queryCriteria;
    }


    /**
     * 标志字典数量获取
     * @return
     */
    private QueryCriteria getCriterionQueryCriteria(){
        QueryCriteria queryCriteria = new QueryCriteria();
        ObjectNode criteria = JsonNodeFactory.instance.objectNode();
        criteria.put("valid", 1);
        criteria.put("groupCode", "0");
        criteria.put("projectId", "0");
        queryCriteria.setCriteria(criteria);
        queryCriteria.setSize(-1L);
        return queryCriteria;
    }

}
