package com.persagy.adm.diagram.core.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.persagy.adm.diagram.client.DicClient;
import com.persagy.adm.diagram.client.RwdClient;
import com.persagy.adm.diagram.constant.Constants;
import com.persagy.adm.diagram.core.DataStrategy;
import com.persagy.adm.diagram.core.model.Diagram;
import com.persagy.adm.diagram.core.model.legend.Legend;
import com.persagy.adm.diagram.core.model.template.DiagramTemplate;
import com.persagy.adm.diagram.dao.DiagramMapper;
import com.persagy.adm.diagram.dao.DiagramTypeMapper;
import com.persagy.adm.diagram.dao.LegendMapper;
import com.persagy.adm.diagram.dao.TemplateMapper;
import com.persagy.adm.diagram.entity.DiagramEntity;
import com.persagy.adm.diagram.entity.DiagramType;
import com.persagy.adm.diagram.entity.LegendEntity;
import com.persagy.adm.diagram.entity.ModelAdapter;
import com.persagy.adm.diagram.entity.TemplateEntity;
import com.persagy.adm.diagram.frame.BdtpRequest;
import com.persagy.adm.diagram.util.ServiceUtil;
import com.persagy.dmp.basic.model.QueryCriteria;
import com.persagy.dmp.basic.model.QueryOperator;
import com.persagy.dmp.basic.utils.JsonNodeUtils;
import com.persagy.dmp.common.constant.ResponseCode;
import com.persagy.dmp.common.exception.BusinessException;
import com.persagy.dmp.common.model.response.CommonResult;
import com.persagy.dmp.digital.client.DigitalObjectFacade;
import com.persagy.dmp.digital.client.DigitalRelationFacade;
import com.persagy.dmp.digital.entity.ObjectDigital;
import com.persagy.dmp.digital.entity.ObjectRelation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * 相关接口实现
 *
 * @author liyang
 * @date 2021-12-31
 */
@Service("dataStrategyImpl")
public class DataStrategyImpl implements DataStrategy {

    @Autowired
    private DicClient dicClient;
    @Autowired
    private RwdClient rwdClient;
    @Autowired
    private ModelAdapter modelAdapter;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private DiagramTypeMapper diagramTypeMapper;
    @Autowired
    private TemplateMapper templateMapper;
    @Autowired
    private LegendMapper legendMapper;
    @Autowired
    private DiagramMapper diagramMapper;
    @Autowired
    @Qualifier("customThreadPool")
    private ThreadPoolExecutor executor;

    /**
     * 获取系统图类型列表
     *
     * @return 系统图类型列表
     */
    @Override
    public List<DiagramType> getDiagramTypes() {
        return diagramTypeMapper.getDiagramTypes();
    }

    /**
     * 获取系统图模板列表
     *
     * @return 模板列表
     */
    @Override
    public List<DiagramTemplate> getTemplates() {
        List<TemplateEntity> templates = templateMapper.getTemplates();
        if (CollectionUtil.isNotEmpty(templates)) {
            return templates.stream().map(templateEntity ->
                    modelAdapter.toTemplate(templateEntity)).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    /**
     * 根据系统图类型获取系统图模板列表
     *
     * @param diagramType 类型
     * @return 模板列表
     */
    @Override
    public List<DiagramTemplate> getTemplates(String diagramType) {
        if (StrUtil.isBlank(diagramType)) {
            throw new BusinessException(ResponseCode.A0400.getCode(), "diagramType不能为空");
        }
        List<TemplateEntity> templates = templateMapper.getTemplatesByType(diagramType);
        if (CollectionUtil.isNotEmpty(templates)) {
            return templates.stream().map(templateEntity ->
                    modelAdapter.toTemplate(templateEntity)).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    /**
     * 保存系统图模板
     *
     * @param template 系统图模板信息
     * @return
     */
    @Override
    public DiagramTemplate saveTemplate(DiagramTemplate template) {
        if (StrUtil.isNotBlank(template.getId())) {
            TemplateEntity dbEntity = templateMapper.selectById(template.getId());
            if (dbEntity != null) {
                templateMapper.updateById(modelAdapter.toTemplateEntity(template));
                return template;
            }
        }
        template.setId(IdUtil.simpleUUID());
        templateMapper.saveTemplate(modelAdapter.toTemplateEntity(template));
        return template;
    }

    /**
     * 根据模板id删除模板
     *
     * @param templateId 模板id
     * @return 是否成功
     */
    @Override
    public boolean deleteTemplate(String templateId) {
        templateMapper.deleteByTemplateId(templateId);
        return true;
    }

    /**
     * 根据模板id获取模板信息
     *
     * @param templateId 模板id
     * @return 系统图模板信息
     */
    @Override
    public DiagramTemplate getTemplate(String templateId) {
        TemplateEntity templateEntity = templateMapper.selectByTemplateId(templateId);
        return templateEntity != null ? modelAdapter.toTemplate(templateEntity) : new DiagramTemplate();
    }

    /**
     * 根据系统code获取图例
     *
     * @param systemCode 所属系统code
     * @return 图例列表
     */
    @Override
    public List<Legend> getLegends(String systemCode) {
        if (StrUtil.isBlank(systemCode)) {
            throw new BusinessException(ResponseCode.A0400.getCode(), "systemCode不能为空");
        }
        List<LegendEntity> legends = legendMapper.getLegends(systemCode);
        if (CollectionUtil.isNotEmpty(legends)) {
            return legends.stream().map(legendEntity ->
                    modelAdapter.toLegend(legendEntity)).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    /**
     * 保存图例
     *
     * @param legend     图例信息
     * @param systemCode 所属系统code
     * @return
     */
    @Override
    public Legend saveLegend(Legend legend, String systemCode) {
        legend.setSystem(systemCode);
        if (StrUtil.isNotBlank(legend.getId())) {
            LegendEntity dbEntity = legendMapper.selectById(legend.getId());
            if (null != dbEntity) {
                legendMapper.updateById(modelAdapter.toLegendEntity(legend));
                return legend;
            }
        }
        legend.setId(IdUtil.simpleUUID());
        legendMapper.saveLegend(modelAdapter.toLegendEntity(legend));
        return legend;
    }

    /**
     * 删除图例
     *
     * @param legendId   图例id
     * @param systemCode 所属系统code
     * @return
     */
    @Override
    public boolean deleteLegend(String legendId, String systemCode) {
        return legendMapper.deleteLegend(legendId, systemCode);
    }

    /**
     * 根据图例id、所属系统code 获取图例信息
     *
     * @param legendId   图例id
     * @param systemCode 所属系统code
     * @return
     */
    @Override
    public Legend getLegend(String legendId, String systemCode) {
        LegendEntity legendEntity = legendMapper.getLegend(legendId, systemCode);
        return legendEntity == null ? new Legend() : modelAdapter.toLegend(legendEntity);
    }

    /**
     * 根据设备类型获取图例列表
     *
     * @param equipmentType 设备类型
     * @return 图例列表
     */
    @Override
    public List<Legend> getLegendsForEquipment(String equipmentType) {
        if (StrUtil.isBlank(equipmentType)) {
            throw new BusinessException(ResponseCode.A0400.getCode(), "equipmentType不能为空");
        }
        String systemCode = Optional.of(equipmentType).map(s -> s.substring(0, 4)).orElse(StrUtil.EMPTY);
        return getLegends(systemCode);
    }

    /**
     * 根据项目id、系统id、集团code 获取系统图列表
     *
     * @param projectId 项目id
     * @param systemId  系统id
     * @param groupCode 集团code
     * @return 系统图列表
     */
    @Override
    public List<Diagram> getDiagrams(String projectId, String systemId, String groupCode) {
        List<DiagramEntity> diagrams = diagramMapper.getDiagrams(null, projectId, systemId, groupCode, null);
        if (CollectionUtil.isNotEmpty(diagrams)) {
            return diagrams.stream().map(diagramEntity ->
                    modelAdapter.toDiagram(diagramEntity)).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    /**
     * 根据系统图类型、项目id、系统id、集团code 获取系统图列表
     *
     * @param diagramType 统图类型
     * @param projectId   项目id
     * @param systemId    系统id
     * @param groupCode   集团code
     * @param name        系统图名称
     * @return 系统图列表
     */
    @Override
    public List<Diagram> getDiagrams(String diagramType, String projectId,
                                     String systemId, String groupCode, String name) {

        Set<DiagramEntity> result = new HashSet<>();
        //所有系统实例
        List<ObjectNode> objectNodes = loadSystemInstance(projectId, groupCode);
        //名称不为空时
        if (StrUtil.isNotBlank(name)) {
            //获取所有id
            List<String> ids = objectNodes.stream().filter(node -> {
                String objectName = Optional.ofNullable(node.get(Constants.NAME)).map(JsonNode::asText).orElse(StrUtil.EMPTY);
                return StrUtil.contains(objectName, name);
            }).map(jsonNodes -> jsonNodes.get(Constants.ID).asText()).filter(Objects::nonNull).collect(Collectors.toList());

            if (CollectionUtil.isNotEmpty(ids)) {
                //由系统实例名模糊匹配到的系统图列表
                List<DiagramEntity> diagramsBySystemIds = diagramMapper.getDiagramsBySystemIds(ids);
                //合并
                result.addAll(diagramsBySystemIds);
            }
        }
        //直接去系统图表根据名称模糊查询查出来的数据
        List<DiagramEntity> diagrams = diagramMapper.getDiagrams(diagramType,
                projectId, systemId, groupCode, name);
        //合并
        result.addAll(diagrams);

        //转成map
        Map<String, ObjectNode> map = objectNodes.stream().collect(Collectors.toMap(node
                -> node.get(Constants.ID).asText(), node -> node));

        return result.stream().filter(Objects::nonNull).map(diagramEntity -> {
            Diagram diagram = modelAdapter.toDiagram(diagramEntity);
            ObjectNode node = map.get(diagramEntity.getSystemId());
            if (node != null) {
                diagram.setExtraProp("systemInstanceName", node.get(Constants.NAME).asText());
            }
            return diagram;
        }).collect(Collectors.toList());

    }

    /**
     * 保存系统图
     *
     * @param diagram 系统图信息
     * @return
     */
    @Override
    public Diagram saveDiagram(Diagram diagram) {
        if (StrUtil.isNotBlank(diagram.getId())) {
            DiagramEntity dbEntity = diagramMapper.selectById(diagram.getId());
            if (null != dbEntity) {
                diagramMapper.updateById(modelAdapter.toDiagramEntity(diagram));
                return diagram;
            }
        }
        diagram.setId(IdUtil.simpleUUID());
        diagramMapper.saveDiagram(modelAdapter.toDiagramEntity(diagram));
        return diagram;
    }

    /**
     * 根据系统图id删除
     *
     * @param diagramId 系统图id
     * @return 是否成功
     */
    @Override
    public boolean deleteDiagram(String diagramId) {
        return diagramMapper.deleteByDiagramId(diagramId);
    }

    /**
     * 根据系统图id获取系统图信息
     *
     * @param diagramId 系统图id
     * @return 系统图信息
     */
    @Override
    public Diagram getDiagram(String diagramId) {
        DiagramEntity diagramEntity = diagramMapper.getDiagram(diagramId);
        return diagramEntity == null ? new Diagram() : modelAdapter.toDiagram(diagramEntity);
    }

    /**
     * 根据设备类型、项目id、系统id、集团code获取对象列表信息
     *
     * @param equipmentTypes 设备类型列表
     * @param projectId      项目id
     * @param systemId       系统id
     * @param groupCode      集团code
     * @return 节点列表
     */
    @Override
    public List<ObjectNode> loadObjectsByType(List<String> equipmentTypes, String projectId,
                                              String systemId, String groupCode) {
        QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper, Constants.EQUIPMENT);
        if (CollectionUtil.isNotEmpty(equipmentTypes)) {
            ArrayNode types = criteria.getCriteria().putArray(ObjectDigital.CLASS_CODE_HUM);
            equipmentTypes.forEach(types::add);
        }

        if (StrUtil.isNotBlank(systemId)) {
            ObjectNode on = JsonNodeFactory.instance.objectNode();
            on.put(ObjectRelation.GRAPH_CODE_HUM, "MechSubset");
            on.put(ObjectRelation.REL_CODE_HUM, "Sy2Eq");
            on.put(ObjectRelation.OBJ_FROM_HUM, systemId);
            criteria.getCriteria().set(QueryOperator.RELATION_TO.getIndex(), on);
        }

        return DigitalObjectFacade.query(groupCode, projectId, null, null, criteria);
    }

    /**
     * 根据关系类型列表、节点id列表、项目id、集团code获取对象关系列表
     *
     * @param relationTypes 关系类型列表
     * @param objectIds     节点id列表
     * @param projectId     项目id
     * @param groupCode     集团code
     * @return 关系列表
     */
    @Override
    public List<ObjectNode> loadRelationsByType(List<String[]> relationTypes, List<String> objectIds,
                                                String projectId, String groupCode) {
        if (CollectionUtil.isEmpty(objectIds)) {
            throw new BusinessException(ResponseCode.A0400.getCode(), "objectIds不能为空");
        }
        //查询到的所有关系
        List<ObjectRelation> allRelations = queryRelationsByObjIds(projectId, groupCode, objectIds);
        //所有关系类型
        Set<String> types = new HashSet<>();
        Optional.ofNullable(relationTypes).ifPresent(relations ->
                relations.forEach(strings -> types.addAll(Arrays.asList(strings))));
        Set<String> typeSet = types.stream().filter(Objects::nonNull).collect(Collectors.toSet());
        //最终符合结果的关系
        if (CollectionUtil.isNotEmpty(typeSet)) {
            List<ObjectRelation> res = allRelations.stream().filter(objectRelation ->
                    typeSet.contains(objectRelation.getRelCode())).collect(Collectors.toList());
            //转换并返回
            return JsonNodeUtils.toListNode(res, null, null);
        }
        return JsonNodeUtils.toListNode(allRelations, null, null);
    }

    /**
     * 查询obj_from 和 objTo 的数据
     *
     * @param projectId 项目id
     * @param groupCode 集团code
     * @param objectIds 对象id列表
     * @return
     */
    private List<ObjectRelation> queryRelationsByObjIds(String projectId, String groupCode, List<String> objectIds) {
        List<ObjectRelation> resultList = new ArrayList<>();
        //obj_from
        CompletableFuture<List<ObjectRelation>> fromFuture = CompletableFuture.supplyAsync(() -> {
            QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper);
            ArrayNode objFrom = criteria.getCriteria().putArray(ObjectRelation.OBJ_FROM_HUM);
            Optional.ofNullable(objectIds).ifPresent(strings -> strings.forEach(objFrom::add));
            return DigitalRelationFacade.query(groupCode, projectId, null, null, criteria);
        }, executor);
        //obj_to
        CompletableFuture<List<ObjectRelation>> toFuture = CompletableFuture.supplyAsync(() -> {
            QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper);
            ArrayNode objTo = criteria.getCriteria().putArray(ObjectRelation.OBJ_TO_HUM);
            Optional.ofNullable(objectIds).ifPresent(strings -> strings.forEach(objTo::add));
            return DigitalRelationFacade.query(groupCode, projectId, null, null, criteria);
        }, executor);

        CompletableFuture.allOf(fromFuture, toFuture).join();

        try {
            resultList.addAll(fromFuture.get());
            resultList.addAll(toFuture.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultList.stream().distinct().filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 根据对象id列表获取节点列表
     *
     * @param objectIds 节点id列表
     * @param projectId 项目id
     * @param groupCode 集团code
     * @return 节点列表
     */
    @Override
    public List<ObjectNode> loadObjectsById(List<String> objectIds, String projectId, String groupCode) {
        if (CollectionUtil.isEmpty(objectIds)) {
            throw new BusinessException(ResponseCode.A0400.getCode(), "objectIds不能为空");
        }
        QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper, Constants.EQUIPMENT);
        ArrayNode arr = criteria.getCriteria().putArray(Constants.ID);
        objectIds.forEach(arr::add);

        return DigitalObjectFacade.query(groupCode, projectId, null, null, criteria);
    }

    /**
     * 根据关系id列表、项目id、集团code获取关系列表
     *
     * @param relationIds 关系id列表
     * @param projectId   项目id
     * @param groupCode   集团code
     * @return 关系列表
     */
    @Override
    public List<ObjectNode> loadRelationsById(List<String> relationIds, String projectId, String groupCode) {
        if (CollectionUtil.isEmpty(relationIds)) {
            throw new BusinessException(ResponseCode.A0400.getCode(), "relationIds不能为空");
        }
        QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper);
        ArrayNode nodes = criteria.getCriteria().putArray(Constants.ID);
        relationIds.forEach(nodes::add);

        List<ObjectRelation> result = DigitalRelationFacade.query(groupCode, projectId,
                null, null, criteria);
        return JsonNodeUtils.toListNode(result, null, null);
    }

    /**
     * 获取系统实例列表
     *
     * @param projectId 项目id
     * @param groupCode 集团code
     * @return 系统实例列表
     */
    @Override
    public List<ObjectNode> loadSystemInstance(String projectId, String groupCode) {
        QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper, Constants.SYSTEM);
        List<ObjectNode> result = DigitalObjectFacade.query(groupCode, projectId, null, null, criteria);
        return result == null ? new ArrayList<>() : result;
    }

    /**
     * 项目下的专业 -> 系统 ->系统实例 的树状结构数据
     *
     * @return 列表
     */
    @Override
    public List<ObjectNode> loadMajorSystemInstance() {
        BdtpRequest req = BdtpRequest.getCurrent();
        String groupCode = req.getGroupCode();
        String projectId = req.getProjectId();
        String appId = req.getAppId();
        String userId = req.getUserId();
        //查询专业
        CompletableFuture<List<ObjectNode>> majorFuture = CompletableFuture.supplyAsync(() -> {
            QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper);
            criteria.getCriteria().put(Constants.TYPE, Constants.MAJOR);
            CommonResult<List<ObjectNode>> result = dicClient.dataQuery(groupCode, projectId,
                    appId, userId, criteria);
            return result.getData();
        }, executor);
        //查询系统
        CompletableFuture<List<ObjectNode>> systemFuture = CompletableFuture.supplyAsync(() -> {
            QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper, Constants.SYSTEM);
            CommonResult<List<ObjectNode>> result = rwdClient.queryObjectType(groupCode, projectId,
                    appId, userId, criteria);
            return result.getData();
        }, executor);
        //查询系统实例
        CompletableFuture<List<ObjectNode>> instanceFuture = CompletableFuture.supplyAsync(() -> {
            QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper, Constants.SYSTEM);
            return DigitalObjectFacade.query(groupCode, projectId, appId, userId, criteria);
        }, executor);

        CompletableFuture.allOf(majorFuture, systemFuture, instanceFuture).join();

        List<ObjectNode> result = null;
        try {
            result = buildMajorSystemInstance(majorFuture.get(), systemFuture.get(), instanceFuture.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 组装树状结构
     *
     * @param majorNodes    专业节点列表
     * @param systemNodes   系统节点列表
     * @param instanceNodes 系统实例节点列表
     * @return
     */
    private List<ObjectNode> buildMajorSystemInstance(List<ObjectNode> majorNodes, List<ObjectNode> systemNodes,
                                                      List<ObjectNode> instanceNodes) {
        //TODO 是否会存在有些系统没有对应的专业、有些实例没有对应的系统
        //组装系统和系统实例的树状结构
        Optional.ofNullable(systemNodes).ifPresent(nodes -> nodes.stream().filter(Objects::nonNull).forEach(node -> {
            String systemCode = node.get(Constants.CODE).asText();
            Optional.ofNullable(instanceNodes).ifPresent(items -> {
                List<ObjectNode> childrenList = items.stream().filter(Objects::nonNull).filter(item -> {
                    JsonNode jsonNode = item.get(ObjectDigital.CLASS_CODE_HUM);
                    return jsonNode != null && StrUtil.equalsIgnoreCase(jsonNode.asText(), systemCode);
                }).collect(Collectors.toList());
                ArrayNode children = node.putArray(Constants.CHILDREN);
                children.addAll(childrenList);
            });
        }));
        //组装专业和系统的树状结构
        Optional.ofNullable(majorNodes).ifPresent(nodes -> nodes.stream().filter(Objects::nonNull).forEach(node -> {
            String majorCode = node.get(Constants.CODE).asText();
            Optional.ofNullable(systemNodes).ifPresent(items -> {
                List<ObjectNode> childrenList = items.stream().filter(Objects::nonNull).filter(item -> {
                    JsonNode jsonNode = item.get(Constants.MAJOR_CODE);
                    return jsonNode != null && StrUtil.equalsIgnoreCase(jsonNode.asText(), majorCode);
                }).collect(Collectors.toList());
                ArrayNode children = node.putArray(Constants.CHILDREN);
                children.addAll(childrenList);
            });
        }));
        return majorNodes;
    }

    /**
     * 根据系统类获取系统类型下的模板列表  以树状结构数据返回
     *
     * @param diagram diagram.getSystem() 参数
     * @return
     */
    @Override
    public List<ObjectNode> loadSystemTypeTemplate(Diagram diagram) {
        if (diagram == null || StrUtil.isBlank(diagram.getSystem())) {
            throw new BusinessException(ResponseCode.A0400.getCode(), "系统类[system]不能为空");
        }
        String system = diagram.getSystem();
        List<DiagramType> diagramTypes = diagramTypeMapper.getDiagramTypes();
        //符合system的系统图类型数据
        List<DiagramType> diagramTypeList = Optional.ofNullable(diagramTypes).map(types -> types.stream().filter(diagramType ->
                diagramType.getDSystem().equalsIgnoreCase(system)).collect(Collectors.toList())).orElse(new ArrayList<>());

        List<TemplateEntity> templates = templateMapper.getTemplates();
        List<ObjectNode> objectNodes = JsonNodeUtils.toListNode(diagramTypeList, null, null);
        Optional.ofNullable(objectNodes).ifPresent(nodes -> nodes.forEach(node -> {
            JsonNode jsonNode = node.get(Constants.CODE);
            ArrayNode children = node.putArray(Constants.CHILDREN);
            if (jsonNode != null) {
                List<TemplateEntity> templateEntities = templates.stream().filter(templateEntity ->
                        templateEntity.getDiagramType().equalsIgnoreCase(jsonNode.asText())).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(templateEntities)) {
                    List<ObjectNode> templateNodes = JsonNodeUtils.toListNode(templateEntities, null, null);
                    children.addAll(templateNodes);
                }
            }
        }));

        return objectNodes;
    }
}
