package com.persagy.proxy.migration.handler;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.node.ArrayNode;
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.DigitalGraphDefineFacade;
import com.persagy.dmp.define.client.DigitalRelationDefineFacade;
import com.persagy.dmp.define.entity.GraphDefine;
import com.persagy.dmp.define.entity.RelationDefine;
import com.persagy.dmp.digital.client.DigitalRelationFacade;
import com.persagy.dmp.digital.entity.ObjectRelation;
import com.persagy.dmp.rwd.migrate.entity.SyncData;
import com.persagy.proxy.adm.constant.AdmCommonConstant;
import com.persagy.proxy.adm.request.AdmResponse;
import com.persagy.proxy.common.entity.InstanceUrlParam;
import com.persagy.proxy.migration.constant.MiGrationRelCode;
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.concurrent.*;
import java.util.stream.Collectors;

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

    private static ObjectRelationMigration instance;

    private ObjectRelationMigration() {}

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

    /**
     * 数据迁移
     *
     * @param context
     * @param migrationInfo
     * @return
     */
    @Override
    public List<DataMigrationExcel> migrateForExcel(InstanceUrlParam context, MigrationInfo migrationInfo) {
        log.info("######################### dt_relation 数据迁移内容获取开始 #########################");
        long start = System.currentTimeMillis();
        List<DataMigrationExcel> dataMigrationExcels = new ArrayList<>();

        //TODO 之后考虑其他方式
        ExecutorService service = getExcecutor();
        List<Future<List<DataMigrationExcel>>> list = new ArrayList<>();

        for (MiGrationRelCode miGrationRelCode: MiGrationRelCode.values()){
            log.info("######################### dt_relation "+miGrationRelCode.getCode()+"数据迁移内容获取开始 #########################");

            Future<List<DataMigrationExcel>> future = service.submit(new Callable<List<DataMigrationExcel>>(){
                @Override
                public List<DataMigrationExcel> call() throws Exception {
                    return startMigrateForExcel(context, migrationInfo, miGrationRelCode.getCode());
                }
            });
            list.add(future);
        }
        service.shutdown();
        for (Future<List<DataMigrationExcel>> future : list) {
            try {
                List<DataMigrationExcel> excelList = future.get();
                if(CollUtil.isNotEmpty(excelList)){
                    dataMigrationExcels.addAll(excelList);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
                log.error("######################### dt_relation 数据迁移内容获取失败"+e.getMessage());
            } catch (ExecutionException e) {
                e.printStackTrace();
                log.error("######################### dt_relation 数据迁移内容获取失败"+e.getMessage());
            }
        }
        long end = System.currentTimeMillis();
        log.info("######################### dt_relation 数据迁移内容获取结束 "+(end-start)+"#########################");
        return dataMigrationExcels;
    }

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

        for (MiGrationRelCode miGrationRelCode: MiGrationRelCode.values()){
            log.info("######################### dt_relation "+miGrationRelCode.getCode()+"数据迁移开始 #########################");

            Future<List<SyncData>> future = service.submit(new Callable<List<SyncData>>(){
                @Override
                public List<SyncData> call() throws Exception {
                    return startMigrateForLog(context, migrationInfo, miGrationRelCode.getCode());
                }
            });
            list.add(future);
        }
        service.shutdown();
        for (Future<List<SyncData>> future : list) {
            try {
                List<SyncData> syncDatas = future.get();
                if(CollUtil.isNotEmpty(syncDatas)){
                    syncDataList.addAll(syncDatas);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
                log.error("######################### dt_relation 数据迁移失败"+e.getMessage());
            } catch (ExecutionException e) {
                e.printStackTrace();
                log.error("######################### dt_relation 数据迁移失败"+e.getMessage());
            }
        }
        long end = System.currentTimeMillis();
        log.info("######################### dt_relation 同步结束 "+(end-start)+"#########################");
        return AdmResponse.success();
    }

    private ExecutorService getExcecutor(){
        ExecutorService service = new ThreadPoolExecutor(10, 15, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue(), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r);
                System.out.println("我是线程ObjectRelation：" + r);
                return t;
            }
        }
        );
        return service;
    }
    private QueryCriteria getQueryCriteria(String typeCode){
        QueryCriteria queryCriteria = new QueryCriteria();
        ObjectNode criteria = JsonNodeFactory.instance.objectNode();
        criteria.put("valid", 1);
        criteria.put("relCode", typeCode);
        queryCriteria.setCriteria(criteria);
        return queryCriteria;
    }

    private List<DataMigrationExcel> startMigrateForExcel(InstanceUrlParam context, MigrationInfo migrationInfo, String code){
        QueryCriteria queryCriteria = getQueryCriteria(code);
        List<DataMigrationExcel> dataMigrationExcels = new ArrayList<>();
        List<ObjectRelation> admRelations = getAdmData(context, queryCriteria);

        String queryUrl = requestUrl(context, migrationInfo, MigrationType.QUERY.getCode());
        List<ObjectRelation> projectRelations = getProjectData(queryCriteria, ObjectRelation.class, queryUrl);
        if(CollUtil.isEmpty(admRelations)){
            log.error(" ######################### dt_relation adm已交付:"+ ResponseCode.C0320.getDesc() +" 数据迁移内容获取结束 #########################");
            return dataMigrationExcels;
        }

        if(CollUtil.isEmpty(projectRelations)){
            dataMigrationExcels.addAll(processDataForExcel(admRelations, MigrationType.CREATE.getCode()));
            return dataMigrationExcels;
        }
        List<ObjectNode> objectNodeListPro = JsonNodeUtils.toListNode(projectRelations, null, null);
        Map<String,Object> projectDefineMap = toEntityMap(objectNodeListPro, ObjectRelation.class);
        List<ObjectNode> objectNodeListAdm = JsonNodeUtils.toListNode(admRelations, null, null);
        Map<String,Object> admDefineMap = toEntityMap(objectNodeListAdm, ObjectRelation.class);

        //差集 新增
        Map<String,Object> doSubtractFromInsert = doSubtractAdd(admDefineMap, projectDefineMap);
        if(!CollUtil.isEmpty(doSubtractFromInsert)){
            List<ObjectRelation> insertData = toList(doSubtractFromInsert, admRelations);
            dataMigrationExcels.addAll(processDataForExcel(insertData, MigrationType.CREATE.getCode()));
        }

        //差集 删除
        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<ObjectRelation> deleteData = toListByIds(deleteIds, projectRelations);
            dataMigrationExcels.addAll(processDataForExcel(deleteData, MigrationType.DELETE.getCode()));
        }

        //交集更新
        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<ObjectRelation> updateData = toList(compareData, admRelations);
                dataMigrationExcels.addAll(processDataForExcel(updateData, MigrationType.UPDATE.getCode()));
            }
        }
        processExcelDatas(context.getGroupCode(),context.getProjectId(), dataMigrationExcels);
        return dataMigrationExcels;
    }

    private List<SyncData> startMigrateForLog(InstanceUrlParam context, MigrationInfo migrationInfo, String code){
        QueryCriteria queryCriteria = getQueryCriteria(code);
        List<SyncData> syncDataList = new ArrayList<>();
        List<ObjectRelation> admRelations = getAdmData(context, queryCriteria);

        String queryUrl = requestUrl(context, migrationInfo, MigrationType.QUERY.getCode());
        List<ObjectRelation> projectRelations = getProjectData(queryCriteria, ObjectRelation.class, queryUrl);
        if(CollUtil.isEmpty(admRelations)){
            log.error(" ######################### dt_relation adm已交付:"+ ResponseCode.C0320.getDesc() +" 同步结束 #########################");
        }

        String insertUrl = requestUrl(context, migrationInfo, MigrationType.CREATE.getCode());
        if(CollUtil.isEmpty(projectRelations)){
            DataMigrationResponse dataMigrationResponse = insertBatch(admRelations, ObjectRelation.class, insertUrl);
            syncDataList = processDataForLog(dataMigrationResponse, MigrationType.CREATE.getCode());
        }
        List<ObjectNode> objectNodeListPro = JsonNodeUtils.toListNode(projectRelations, null, null);
        Map<String,Object> projectDefineMap = toEntityMap(objectNodeListPro, ObjectRelation.class);
        List<ObjectNode> objectNodeListAdm = JsonNodeUtils.toListNode(admRelations, null, null);
        Map<String,Object> admDefineMap = toEntityMap(objectNodeListAdm, ObjectRelation.class);

        //差集 新增
        Map<String,Object> doSubtractFromInsert = doSubtractAdd(admDefineMap, projectDefineMap);
        if(!CollUtil.isEmpty(doSubtractFromInsert)){
            List<ObjectRelation> insertData = toList(doSubtractFromInsert, admRelations);
            DataMigrationResponse dataMigrationResponse = insertBatch(insertData, ObjectRelation.class, insertUrl);
            syncDataList.addAll(processDataForLog(dataMigrationResponse, MigrationType.CREATE.getCode()));
        }

        //差集 删除
        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, delUrl);
            syncDataList.addAll(processDataForLog(dataMigrationResponse, MigrationType.DELETE.getCode()));
        }

        //交集更新
        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<ObjectRelation> updateData = toList(compareData, admRelations);
                String updateUrl = requestUrl(context, migrationInfo, MigrationType.UPDATE.getCode());
                DataMigrationResponse dataMigrationResponse = updateBatch(updateData, ObjectRelation.class, updateUrl);
                syncDataList.addAll(processDataForLog(dataMigrationResponse, MigrationType.UPDATE.getCode()));
            }
        }
        return super.addSynLog(context, syncDataList);
    }


    /**
     * 获取adm交付的数据，默认获取有效数据
     *
     * @param context
     * @return
     */
    @Override
    public List<ObjectRelation> getAdmData(InstanceUrlParam context, QueryCriteria queryCriteria) {
        List<ObjectRelation> relations = DigitalRelationFacade.query(context.getGroupCode(),context.getProjectId(),context.getUserId(),context.getUserId(),queryCriteria);
        return relations;
    }

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

    public List<ObjectRelation> toListByIds(Collection<String> ids, List<ObjectRelation> fromList) {
        if(CollUtil.isEmpty(ids) || CollUtil.isEmpty(fromList)){
            return Collections.emptyList();
        }
        List<ObjectRelation> results = new ArrayList<>(ids.size());
        Map<String, ObjectRelation> objectRelationMap = CollUtil.fieldValueMap(fromList, BaseEntity.PROP_ID);
        for(String key : ids){
            results.add(objectRelationMap.get(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.DT_RELATION);
        }else if(type.equals(MigrationType.CREATE.getCode())){
            url.append(MigrationConstant.DT_RELATION_INSERT);
        }else if(type.equals(MigrationType.UPDATE.getCode())){
            url.append(MigrationConstant.DT_RELATION_UPDATE);
        }else{
            url.append(MigrationConstant.DT_RELATION_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){
            if(obj ==  null){
                continue;
            }
            ObjectRelation objectRelation = (ObjectRelation) obj;
            DataMigrationExcel excel = new DataMigrationExcel();
            excel.setId(objectRelation.getId());
            excel.setCode(String.join(StrUtil.DASHED, objectRelation.getGraphCode(), objectRelation.getRelCode()));
            excel.setObjFrom(objectRelation.getObjFrom());
            excel.setObjTo(objectRelation.getObjTo());
            excel.setType(type);
            excel.setCreateTime(objectRelation.getCreationTime().toString());
            excel.setUpdateTime(objectRelation.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()){
                ObjectRelation relation = (ObjectRelation) obj;
                SyncData syncData = new SyncData();
                syncData.setTableName(MigrationTable.DTRELATION.getCode());
                syncData.setType(type);//操作
                syncData.setTargetId(relation.getId());
                syncData.setCode(String.join(StrUtil.DASHED, relation.getGraphCode(), relation.getRelCode()));
                syncData.setFromId(relation.getObjFrom());
                syncData.setToId(relation.getObjTo());
                syncDataList.add(syncData);
            }
        }
        if(CollUtil.isNotEmpty(dataMigrationResponse.getErrorMsg())){
            syncDataList.addAll(addErrorInfoForLog(dataMigrationResponse.getErrorMsg(),MigrationTable.DTRELATION.getCode()));
        }
        return syncDataList;
    }


    /***
     * Description: 查询所有关系类型

     * @return : java.util.Map<java.lang.String,com.persagy.dmp.define.entity.RelationDefine>
     * @author : lijie
     * @date :2021/10/15 11:22
     * Update By lijie 2021/10/15 11:22
     */
    private Map<String, RelationDefine> queryAllRelationDefineMap(String groupCode, String projectId) {
        List<RelationDefine> relationDefines = DigitalRelationDefineFacade.query(groupCode, projectId,
                AdmCommonConstant.APP_ID, AdmCommonConstant.USER_ID, new QueryCriteria());
        if (CollUtil.isEmpty(relationDefines)){
            return new HashMap<>();
        }
        return relationDefines.stream()
                .collect(Collectors.toMap(relationDefine ->
                                relationDefine.getGraphCode() + StrUtil.DASHED + relationDefine.getCode(),
                        relationDefine -> relationDefine, (k1, k2) -> k1));
    }

    /**
     * 将excel中的relcode graphCode 翻译成name组合
     * @param groupCode
     * @param projectId
     * @param excelList
     */
    private void processExcelDatas(String groupCode, String projectId, List<DataMigrationExcel> excelList){
        // 1.获取所有的一级,二级图类型定义
        QueryCriteria parentCriteria = new QueryCriteria();
        List<GraphDefine> graphDefines = DigitalGraphDefineFacade.query(groupCode, projectId,
                AdmCommonConstant.APP_ID, AdmCommonConstant.USER_ID, parentCriteria);
        if (!CollUtil.isEmpty(graphDefines)){
            // 2.查询所有的边类型定义
            Map<String, RelationDefine> relationDefineMap = queryAllRelationDefineMap(groupCode,projectId);
            Map<String, GraphDefine> graphDefineMap = CollUtil.fieldValueMap(graphDefines, "code");
            for(DataMigrationExcel excel : excelList){
                String code = excel.getCode();
                if(StrUtil.isEmpty(code) || !code.contains(StrUtil.DASHED)){
                    continue;
                }
                //excel.setCode(String.join(StrUtil.DASHED, objectRelation.getGraphCode(), objectRelation.getRelCode()));
                String tempName = null;
                String graphName = graphDefineMap.get(code.split(StrUtil.DASHED)[0]).getName();
                String relName = relationDefineMap.get(code).getName();
                if(StrUtil.isAllNotEmpty(graphName, relName)){
                    tempName = String.join(StrUtil.DASHED, graphName, relName);
                }
                excel.setName(tempName==null?"":tempName);
            }

        }
    }

    //删除关系，手动拼接删除条件
    public DataMigrationResponse deleteBatch(List<String> ids, String url) {
        QueryCriteria criteria = new QueryCriteria();
        ObjectNode node = JsonNodeFactory.instance.objectNode();
        //删除关系 然后重建
        ArrayNode arrayNode = node.putArray("id");
        ids.forEach(id -> arrayNode.add(id));
        criteria.setCriteria(node);
        return processProjectData(null, criteria, ObjectRelation.class, url);
    }
}