package com.persagy.proxy.migration.service.Impl;

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.DigitalDefineFacade;
import com.persagy.dmp.define.entity.ObjectInfoDefine;
import com.persagy.dmp.define.entity.ObjectTypeDefine;
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.IDataMigration;
import com.persagy.proxy.migration.service.IDataMigrationProcess;
import com.persagy.proxy.migration.service.IDefineDataMigration;
import com.persagy.proxy.migration.service.Impl.MigrationAbstractServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

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

/**
 * @author lvxianyun
 * @projectName adm-middleware
 * @description: 迁移adm交付的数据到现场，覆盖现场数据，提供增加、删除、修改 记录放入excel; DefineType 数据迁移 adm -> target project
 * @date 2021/12/1 19:38
 */
@Slf4j
@Service
public class DefineTypeMigration extends MigrationAbstractServiceImpl<ObjectTypeDefine> implements IDataMigration, IDefineDataMigration<ObjectTypeDefine>, IDataMigrationProcess<ObjectTypeDefine> {

    /**
     * 数据迁移 迁移项目级 集团级 字典信息
     *
     * @param context
     * @param migrationInfo
     * @return excel
     */
    @Override
    public List<DataMigrationExcel> migrateForExcel(InstanceUrlParam context, MigrationInfo migrationInfo) {
        log.info("######################### DefineType 数据迁移内容获取开始 #########################");
        QueryCriteria queryCriteria = getCustomQueryCriteria(context);

        List<ObjectTypeDefine> admDefineList = getAdmData(context, queryCriteria);
        String queryUrl = requestUrl(context, migrationInfo, MigrationType.QUERY.getCode());
        List<ObjectTypeDefine> projectDefineList = getProjectData(queryCriteria, ObjectTypeDefine.class, queryUrl);
        List<DataMigrationExcel> dataMigrationExcels = compareByData(admDefineList, projectDefineList);
        log.info("######################### DefineType 数据迁移内容获取结束 #########################");
        return dataMigrationExcels;
    }

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

        QueryCriteria queryCriteria = getCustomQueryCriteria(context);

        List<ObjectTypeDefine> admDefineList = getAdmData(context, queryCriteria);
        String queryUrl = requestUrl(context, migrationInfo, MigrationType.QUERY.getCode());
        List<ObjectTypeDefine> projectDefineList = getProjectData(queryCriteria, ObjectTypeDefine.class, queryUrl);
        List<ObjectNode> objectNodeList = JsonNodeUtils.toListNode(projectDefineList, null, null);
        if(CollUtil.isEmpty(admDefineList)){
            log.error(" ######################### DefineType adm已交付需要同步的数据:"+ ResponseCode.C0320.getDesc() +" 同步结束 #########################");
        }

        String insertUrl = requestUrl(context, migrationInfo, MigrationType.CREATE.getCode());
        if(CollUtil.isEmpty(objectNodeList)){
            log.info("######################### DefineType 批量插入数据 #########################");
            DataMigrationResponse dataMigrationResponse = insertBatch(admDefineList, ObjectTypeDefine.class, insertUrl);
            syncDataList.addAll(processDataForLog(dataMigrationResponse, MigrationType.CREATE.getCode()));
        }
        List<ObjectNode> objectNodeListPro = JsonNodeUtils.toListNode(objectNodeList, null, null);
        Map<String,Object> projectDefineMap = toEntityMap(objectNodeListPro, ObjectTypeDefine.class);
        List<ObjectNode> objectNodeListAdm = JsonNodeUtils.toListNode(admDefineList, null, null);
        Map<String,Object> admDefineMap = toEntityMap(objectNodeListAdm, ObjectTypeDefine.class);

        //差集 新增
        log.info("######################### DefineType 批量处理新增数据 #########################");
        Map<String,Object> doSubtractFromInsert = doSubtractAdd(admDefineMap, projectDefineMap);
        if(!CollUtil.isEmpty(doSubtractFromInsert)){
            List<ObjectTypeDefine> insertData = toList(doSubtractFromInsert, admDefineList);
            DataMigrationResponse dataMigrationResponse = insertBatch(insertData, ObjectTypeDefine.class, insertUrl);
            syncDataList.addAll(processDataForLog(dataMigrationResponse, MigrationType.CREATE.getCode()));
        }

        //差集 删除
        log.info("######################### DefineType 批量处理删除数据 #########################");
        Map<String,Object> doSubtractFromDelete = doSubtractDel(admDefineMap, projectDefineMap);
        if(!CollUtil.isEmpty(doSubtractFromDelete)){
            List<String> deleteIds = doSubtractFromDelete.entrySet().stream().map(en -> en.getKey()).collect(Collectors.toList());

            String delUrl = requestUrl(context, migrationInfo, MigrationType.DELETE.getCode());
            DataMigrationResponse dataMigrationResponse = deleteBatch(deleteIds, ObjectTypeDefine.class, delUrl);
            List<String> successIds = (List<String>) dataMigrationResponse.getData();
            List<ObjectTypeDefine> delObjs = toListByIds(successIds, projectDefineList);
            dataMigrationResponse.setData(delObjs);
            syncDataList.addAll(processDataForLog(dataMigrationResponse, MigrationType.DELETE.getCode()));
        }

        //交集更新
        log.info("######################### DefineType 批量处理更新数据 #########################");
        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<ObjectTypeDefine> updateData = toList(compareData, admDefineList);
                String updateUrl = requestUrl(context, migrationInfo, MigrationType.UPDATE.getCode());
                DataMigrationResponse dataMigrationResponse = updateBatch(updateData, ObjectTypeDefine.class, updateUrl);
                syncDataList.addAll(processDataForLog(dataMigrationResponse, MigrationType.UPDATE.getCode()));
            }
        }
        long end = System.currentTimeMillis();
        log.info("######################### DefineType 数据迁移已结束 时间："+(end-start)+" 共计："+syncDataList.size()+"条 #########################");
        super.addSynLog(context, syncDataList);
        return AdmResponse.success();
    }

    /**
     * 数据迁移
     *
     * @param context
     * @return sql 文件
     */
    @Override
    public String migrateForSql(InstanceUrlParam context) {
        //获取自定义的类型数据
        QueryCriteria queryCriteria = getCustomQueryCriteria(context);
        List<ObjectTypeDefine> admDefineList = getAdmData(context, queryCriteria);
        if(CollUtil.isEmpty(admDefineList)){
            return "";
        }
        StringBuffer sqls = new StringBuffer(" \n -- 数据迁移  dt_define_type ------ \n");
        sqls.append(MigrationConstant.DEF_CLASS_SQL_PRE);
        admDefineList.forEach(typeDefine ->{
            sqls.append("(");
            sqls.append("'").append(typeDefine.getId()).append("',");
            sqls.append("'").append(typeDefine.getCode()).append("',");
            sqls.append("'").append(typeDefine.getName()).append("',");
            sqls.append("'").append(typeDefine.getAliasCode()).append("',");
            sqls.append("'").append(typeDefine.getAliasName()).append("',");
            sqls.append("'").append(typeDefine.getObjType()).append("',");
            sqls.append("'").append(typeDefine.getGroupCode()).append("',");
            sqls.append("'").append(typeDefine.getProjectId()).append("',");

            if (typeDefine.getMajorCode() == null) {
                sqls.append(typeDefine.getMajorCode()).append(",");
            } else {
                sqls.append("'").append(typeDefine.getMajorCode()).append("',");
            }

            sqls.append("'").append(typeDefine.getSystemCode()).append("',");

            if (typeDefine.getEquipmentCode() == null) {
                sqls.append(typeDefine.getEquipmentCode()).append(",");
            } else {
                sqls.append("'").append(typeDefine.getEquipmentCode()).append("',");
            }

            sqls.append("'").append(typeDefine.getParentCode()).append("',");
            sqls.append("'").append(typeDefine.getVersion()).append("',");
            sqls.append(typeDefine.getSourceFrom()).append(",");
            sqls.append("'").append(typeDefine.getCreator()).append("',");
            sqls.append("'").append(typeDefine.getCreationTime()).append("',");

            if (typeDefine.getModifier() == null) {
                sqls.append(typeDefine.getModifier()).append(",");
            } else {
                sqls.append("'").append(typeDefine.getModifier()).append("',");
            }

            if (typeDefine.getModifiedTime() == null) {
                sqls.append(typeDefine.getModifiedTime()).append(",");
            } else {
                sqls.append("'").append(typeDefine.getModifiedTime()).append("',");
            }

            sqls.append(typeDefine.getValid());
            sqls.append(")\n ,");
        });
        String sql = sqls.toString();
        sql = sql.substring(0, sql.lastIndexOf(","));
        sql +=";";
        return sql;
    }

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

    /**
     * 根据map转为需要处理的对象集合
     * 使用 MigrationConstant.spliter 作为拼接符
     * @param fromMap
     * @param fromList
     * @return
     */
    public List<ObjectTypeDefine> toList(Map<String, Object> fromMap, List<ObjectTypeDefine> fromList) {
        if(CollUtil.isEmpty(fromMap) || CollUtil.isEmpty(fromList)){
            return Collections.emptyList();
        }
        return toListByIds(fromMap.keySet(), fromList);
    }

    public List<ObjectTypeDefine> toListByIds(Collection<String> ids, List<ObjectTypeDefine> fromList) {
        if(CollUtil.isEmpty(ids) || CollUtil.isEmpty(fromList)){
            return Collections.emptyList();
        }
        List<ObjectTypeDefine> results = new ArrayList<>(ids.size());
        Map<String, ObjectTypeDefine> objectTypeDefineMap = CollUtil.fieldValueMap(fromList, BaseEntity.PROP_ID);
        for(String key : ids){
            results.add(objectTypeDefineMap.get(StrUtil.trim(key)));
        }
        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_CLASS);
        }else if(type.equals(MigrationType.CREATE.getCode())){
            url.append(MigrationConstant.DEF_CLASS_INSERT);
        }else if(type.equals(MigrationType.UPDATE.getCode())){
            url.append(MigrationConstant.DEF_CLASS_UPDATE);
        }else{
            url.append(MigrationConstant.DEF_CLASS_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){
            ObjectTypeDefine objectTypeDefine = (ObjectTypeDefine) obj;
            DataMigrationExcel excel = new DataMigrationExcel();
            excel.setId(objectTypeDefine.getId());
            excel.setCode(objectTypeDefine.getCode());
            excel.setName(objectTypeDefine.getName());
            excel.setType(type);
            excel.setCreateTime(objectTypeDefine.getCreationTime().toString());
            excel.setUpdateTime(objectTypeDefine.getModifiedTime() == null ? "" : objectTypeDefine.getModifiedTime().toString());
            if(objectTypeDefine.getGroupCode().equals("0") && objectTypeDefine.getProjectId().equals("0")){
                excel.setMark("标准字典");
            }else{
                excel.setMark("自定义字典");
            }
            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()){
                ObjectTypeDefine objectTypeDefine = (ObjectTypeDefine) obj;
                SyncData syncData = new SyncData();
                syncData.setTableName(MigrationTable.DTDEFINETYPE.getCode());
                syncData.setTargetId(objectTypeDefine.getId());
                syncData.setName(objectTypeDefine.getName());
                syncData.setCode(objectTypeDefine.getCode());
                syncData.setObjectType(objectTypeDefine.getObjType());
                syncData.setType(type);//操作
                syncDataList.add(syncData);
            }
        }
        if(CollUtil.isNotEmpty(dataMigrationResponse.getErrorMsg())){
            syncDataList.addAll(addErrorInfoForLog(dataMigrationResponse.getErrorMsg(), MigrationTable.DTDEFINETYPE.getCode()));
        }
        return syncDataList;
    }

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

    /**
     * 标准字典 根据整体数量进行比较
     *
     * @param context
     * @param migrationInfo
     * @return
     */
    @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){
            dataMigrationExcels = compareCriterionByData(context, migrationInfo);
            if(CollUtil.isEmpty(dataMigrationExcels)){
                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("######################### DefineType 数据迁移 标准字典验证 开始 #########################");
        QueryCriteria queryCriteria = getCriterionQueryCriteria();
        List<ObjectTypeDefine> admDefineList = getAdmData(context, queryCriteria);
        String queryUrl = requestUrl(context, migrationInfo, MigrationType.QUERY.getCode());
        List<ObjectTypeDefine> projectDefineList = getProjectData(queryCriteria, ObjectTypeDefine.class, queryUrl);
        List<DataMigrationExcel> dataMigrationExcels = compareByData(admDefineList, projectDefineList);
        log.info("######################### DefineType 数据迁移 标准字典验证 结束 #########################");
        return dataMigrationExcels;
    }

    /**
     * 比较数据
     *
     * @param admDefineList
     * @param projectDefineList
     * @return
     */
    @Override
    public List<DataMigrationExcel> compareByData(List<ObjectTypeDefine> admDefineList, List<ObjectTypeDefine> projectDefineList ) {
        long start = System.currentTimeMillis();
        List<DataMigrationExcel> dataMigrationExcels = new ArrayList<>();
        List<ObjectNode> objectNodeList = JsonNodeUtils.toListNode(projectDefineList, null, null);
        if(CollUtil.isEmpty(admDefineList)){
            log.error(" ######################### DefineType adm已交付需要同步的数据:"+ ResponseCode.C0320.getDesc() +" 同步结束 #########################");
            return dataMigrationExcels;
        }

        //String insertUrl = requestUrl(context, migrationInfo, MigrationType.INSERT.getCode());
        if(CollUtil.isEmpty(objectNodeList)){
            log.info("######################### DefineType 批量插入数据获取 #########################");
            //DataMigrationResponse dataMigrationResponse = insertBatch(admDefineList, ObjectTypeDefine.class, insertUrl);
            dataMigrationExcels.addAll(processDataForExcel(admDefineList, MigrationType.CREATE.getCode()));

            return dataMigrationExcels;
        }
        List<ObjectNode> objectNodeListPro = JsonNodeUtils.toListNode(objectNodeList, null, null);
        Map<String,Object> projectDefineMap = toEntityMap(objectNodeListPro, ObjectTypeDefine.class);
        List<ObjectNode> objectNodeListAdm = JsonNodeUtils.toListNode(admDefineList, null, null);
        Map<String,Object> admDefineMap = toEntityMap(objectNodeListAdm, ObjectTypeDefine.class);

        //差集 新增
        log.info("######################### DefineType 批量处理新增数据获取 #########################");
        Map<String,Object> doSubtractFromInsert = doSubtractAdd(admDefineMap, projectDefineMap);
        if(!CollUtil.isEmpty(doSubtractFromInsert)){
            List<ObjectTypeDefine> insertData = toList(doSubtractFromInsert, admDefineList);
            //DataMigrationResponse dataMigrationResponse = insertBatch(insertData, ObjectTypeDefine.class, insertUrl);
            dataMigrationExcels.addAll(processDataForExcel(insertData, MigrationType.CREATE.getCode()));
        }

        //差集 删除
        log.info("######################### DefineType 批量处理删除数据获取 #########################");
        Map<String,Object> doSubtractFromDelete = doSubtractDel(admDefineMap, projectDefineMap);
        if(!CollUtil.isEmpty(doSubtractFromDelete)){
            List<String> deleteIds = doSubtractFromDelete.entrySet().stream().map(en -> en.getKey()).collect(Collectors.toList());
            List<ObjectTypeDefine> deleteData = toListByIds(deleteIds, projectDefineList);

            /*String delUrl = requestUrl(context, migrationInfo, MigrationType.DELETE.getCode());
            DataMigrationResponse dataMigrationResponse = deleteBatch(deleteIds, ObjectTypeDefine.class, delUrl);*/
            dataMigrationExcels.addAll(processDataForExcel(deleteData, MigrationType.DELETE.getCode()));
        }

        //交集更新
        log.info("######################### DefineType 批量处理更新数据获取 #########################");
        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<ObjectTypeDefine> updateData = toList(compareData, admDefineList);
                //String updateUrl = requestUrl(context, migrationInfo, MigrationType.UPDATE.getCode());
                //DataMigrationResponse dataMigrationResponse = updateBatch(updateData, ObjectTypeDefine.class, updateUrl);
                dataMigrationExcels.addAll(processDataForExcel(updateData, MigrationType.UPDATE.getCode()));
            }
        }
        long end = System.currentTimeMillis();
        log.info("######################### DefineType 数据迁移获取已结束 时间："+(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;
    }
}
