package com.persagy.proxy.object.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
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.dmp.basic.model.QueryCriteria;
import com.persagy.dmp.common.model.entity.BaseEntity;
import com.persagy.dmp.digital.client.DigitalRelationFacade;
import com.persagy.dmp.digital.entity.ObjectRelation;
import com.persagy.proxy.adm.constant.AdmCommonConstant;
import com.persagy.proxy.adm.constant.AdmRelationTypeEnum;
import com.persagy.proxy.adm.request.AdmQueryCriteria;
import com.persagy.proxy.adm.request.AdmResponse;
import com.persagy.proxy.adm.service.IAdmRelationService;
import com.persagy.proxy.adm.service.impl.AbstractAdmBaseServiceImpl;
import com.persagy.proxy.adm.utils.AdmContextUtil;
import com.persagy.proxy.adm.utils.AdmEntityTransferUtil;
import com.persagy.proxy.calculation.service.AdmRelCalService;
import com.persagy.proxy.common.client.DmpRwdClient;
import com.persagy.proxy.common.entity.DmpResult;
import com.persagy.proxy.common.entity.InstanceUrlParam;
import com.persagy.proxy.common.entity.ObjRelationDTO;
import com.persagy.proxy.common.entity.RelationDTO;
import com.persagy.proxy.dictionary.model.AdmDict;
import com.persagy.proxy.dictionary.model.AdmObjectType;
import com.persagy.proxy.dictionary.service.IAdmDictService;
import com.persagy.proxy.object.model.AdmEquipment;
import com.persagy.proxy.object.model.AdmProperty;
import com.persagy.proxy.object.service.IAdmComAndEquipService;
import com.persagy.proxy.object.service.IAdmEquipmentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * ADM 设备 实现
 * @author lvxy
 * @date 2021/8/18
 */
@Slf4j
@Service
public class AdmEquipmentServiceImpl extends AbstractAdmBaseServiceImpl<AdmEquipment> implements IAdmEquipmentService, IAdmComAndEquipService {

    @Autowired
    private DmpRwdClient rwdClient;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private IAdmDictService dictService;
    @Autowired
    private AdmRelCalService admRelCalService;
    @Autowired
    private IAdmRelationService relationService;

    @Override
    public List<AdmEquipment> doInsertEuip(InstanceUrlParam context, Class<AdmEquipment> clazz, List<AdmEquipment> voList) {
        if(CollUtil.isEmpty(voList)) {
            return null;
        }
        return doInsert(context, clazz, voList);
    }

    @Override
    public List<AdmEquipment> doUpdateEuip(InstanceUrlParam context, Class<AdmEquipment> clazz, List<AdmEquipment> voList) {
        // 处理关系标记
        processRelationType(voList);
        Map<String, AdmEquipment> voMap = CollUtil.fieldValueMap(voList, BaseEntity.PROP_ID);
        //查询设备对应的资产 关系 Pe2Eq （1:N,一个资产关联多个设备，设备是最小单位 by:lvxianyun）
        DmpResult<List<ObjRelationDTO>> relationRes = queryRelation(context, voMap);
        boolean isDealDataSucc = false;
        //不是绑定关系，清理绑定关系，重新绑定
        if(CollUtil.isNotEmpty(relationRes.getData())){
            isDealDataSucc = updateRelation2Pe(context, relationRes, voMap);
        }else {
            //不存在关系,需要新绑定关系，更新per,插入关系
            isDealDataSucc = insertRelation2Pe(context, voList, voMap);
        }
        return isDealDataSucc ? doUpdate(context, clazz, voList) : null;
    }

    @Override
    public void doDeleteEuip(InstanceUrlParam context, List<AdmEquipment> voList) {
        Map<String, AdmEquipment> voMap = CollUtil.fieldValueMap(voList, BaseEntity.PROP_ID);
        DmpResult<List<ObjRelationDTO>> relationRes = queryRelation(context, voMap);
        //清理绑定关系
        if(CollUtil.isNotEmpty(relationRes.getData())){
            updateRelation2Pe(context, relationRes, voMap);
        }
        doDelete(context,voList);
    }

    @Override
    public List<AdmEquipment> doInsert(InstanceUrlParam context, Class<AdmEquipment> clazz, List<AdmEquipment> voList) {
        processFloorRelations(context, voList, false);
        return super.doInsert(context, clazz, voList);
    }

    @Override
    public List<AdmEquipment> doUpdate(InstanceUrlParam context, Class<AdmEquipment> clazz, List<AdmEquipment> voList) {
        processFloorRelations(context, voList, false);
        List<AdmEquipment> equipmentList = super.doUpdate(context, clazz, voList);
        // 处理关系标记
        processRelationType(equipmentList);
        return equipmentList;
    }

    @Override
    public void doDelete(InstanceUrlParam context, List<AdmEquipment> voList) {
        processFloorRelations(context, voList, true);
        super.doDelete(context, voList);
    }

    /**
     * 同步设备相关的关系
     * 先删除后新增
     * @param context
     * @param voList
     */
    private void processFloorRelations(InstanceUrlParam context, List<AdmEquipment> voList, boolean isDelete) {
        if(CollUtil.isEmpty(voList)) {
            return;
        }
        // 两个关系的graphCode一样
        String graphCode = AdmRelationTypeEnum.EQ2BD.getGraphCode();
        String buildingRelCode = AdmRelationTypeEnum.EQ2BD.getRelCode();
        String floorRelCode = AdmRelationTypeEnum.EQ2FL.getRelCode();
        // 删除设备相关关系的条件
        QueryCriteria criteria = new QueryCriteria();
        criteria.setCriteria(JsonNodeFactory.instance.objectNode());
        criteria.getCriteria().put("graphCode", graphCode);
        criteria.getCriteria().putArray("relCode").add(buildingRelCode).add(floorRelCode);
        ArrayNode arrayNode = criteria.getCriteria().putArray("objFrom");
        // 待新增关系
        List<RelationDTO> relations = new ArrayList();
        for(AdmEquipment vo:voList) {
            // 如果为删除或是清除buildingId和floorId，则直接删除
            if(isDelete || CollUtil.containsAll(vo.getNullList(), CollUtil.newHashSet("buildingId", "floorId"))) {
                arrayNode.add(vo.getId());
                continue;
            }
            // 不用增加关系
            if(StrUtil.isBlank(vo.getBuildingId())) {
                continue;
            }
            // 先清除原来的关系
            arrayNode.add(vo.getId());
            // 增加BD关系
            relations.add(RelationDTO.builder().graphCode(graphCode).relCode(buildingRelCode).objFrom(vo.getId()).objTo(vo.getBuildingId()).build());
            // 如果有删除楼层关系
            if(StrUtil.isBlank(vo.getFloorId()) || CollUtil.contains(vo.getNullList(), "floorId")) {
                continue;
            }
            // 增加FL关系
            relations.add(RelationDTO.builder().graphCode(graphCode).relCode(floorRelCode).objFrom(vo.getId()).objTo(vo.getFloorId()).build());
        }
        // 先删除旧关系
        if(arrayNode.size() > 0) {
            relationService.doDelete(context, criteria);
        }
        // 再创建新关系
        if(CollUtil.isNotEmpty(relations)) {
            relationService.doSave(context, relations);
        }
    }

    @Override
    public AdmResponse doQuery(InstanceUrlParam context, AdmQueryCriteria request, Class<AdmEquipment> clazz) {
        AdmResponse response = super.doQuery(context, request, clazz);
        // 系统需要补充分类名称属性
        ensureSpecialValues(context, (List<AdmEquipment>) response.getContent());
        return response;
    }

    @Override
    public void ensureSpecialValues(InstanceUrlParam context, List<AdmEquipment> vos) {
        if(CollUtil.isEmpty(vos)) {
            return;
        }
        // 查询对象类型 - 只找到这个接口，应提供一个按编码查编码名称Map的接口
        AdmDict admDict = AdmDict.builder().type(AdmEquipment.OBJ_TYPE).build();
        AdmResponse dicRs = dictService.queryCategory(AdmContextUtil.toDmpContext(), admDict, AdmObjectType.class);
        List<AdmObjectType> dicList = (List<AdmObjectType>) dicRs.getContent();
        if(CollUtil.isEmpty(dicList)) {
            return;
        }
        // 转换为编码名称Map
        Map<String, String> codeNameMap = CollUtil.fieldValueAsMap(dicList, "code", "name");
        // 赋值
        for(AdmEquipment vo:vos) {
            String classCode = vo.getClassCode();
            if(StrUtil.isEmpty(classCode)) {
                continue;
            }
            Map<String, String> category = new HashMap<>(16);
            category.put("code", classCode);
            category.put("name", codeNameMap.get(vo.getClassCode()));
            vo.setEquipCategory(category);
        }
    }
    /***
     * Description: 将原来重写的doQuery方法改为该方法,用于使用钩子方法调整查询参数
     * @param admQueryCriteria : 请求参数
     * @return : com.persagy.proxy.adm.request.AdmResponse
     * @author : lijie
     * @date :2021/10/29 11:42
     * Update By lijie 2021/10/29 11:42
     */
    @Override
    public AdmResponse query(AdmQueryCriteria admQueryCriteria) {
        AdmResponse response = super.doQuery(AdmContextUtil.toDmpContext(), admQueryCriteria, AdmEquipment.class);
        // 系统需要补充分类名称属性
        ensureSpecialValues(AdmContextUtil.toDmpContext(), (List<AdmEquipment>) response.getContent());
        return response;
    }

    @Override
    protected void processDmpCriteria(QueryCriteria dmpRequest) {
        /*ObjectNode criteria = dmpRequest.getCriteria();
        if (null==criteria){
            return;
        }
        if (criteria.has("floorId")){
            // 有楼层id过滤
            if(!criteria.get("floorId").toString().contains("$null")){
                ObjectMapper objectMapper = SpringHelper.getBean(ObjectMapper.class);
                ObjectNode relationFrom = objectMapper.createObjectNode();
                relationFrom.put("graphCode", GraphCodeEnum.MechInArch.name());
                relationFrom.put("relCode", RelCodeEnum.Eq2Fl.name());
                relationFrom.set("objTo",criteria.get("floorId"));
                criteria.set("relationFrom",relationFrom);
                criteria.remove("floorId");
                if (criteria.has("buildingId"))
                    criteria.remove("buildingId");
                return;
            }
        }
        if (criteria.has("buildingId")){
            // 有建筑id过滤
            ObjectMapper objectMapper = SpringHelper.getBean(ObjectMapper.class);
            ObjectNode relationFrom = objectMapper.createObjectNode();
            relationFrom.put("graphCode", GraphCodeEnum.MechInArch.name());
            relationFrom.put("relCode", RelCodeEnum.Eq2Bd.name());
            if(!criteria.get("buildingId").toString().contains("$null")) {
                relationFrom.set("objTo", criteria.get("buildingId"));
            }else{
                relationFrom.put("$bindFlag", false);
            }
            criteria.set("relationFrom",relationFrom);
            criteria.remove("buildingId");
        }*/
    }

    private DmpResult<List<ObjRelationDTO>> queryRelation(InstanceUrlParam context, Map<String, AdmEquipment> voMap){
        QueryCriteria dmpRequest = new QueryCriteria();
        ObjectNode criteria = JsonNodeFactory.instance.objectNode();
        ArrayNode array = criteria.putObject("objTo").putArray("$in");
        voMap.keySet().forEach(equipId -> array.add(equipId));
        criteria.put("relCode", "Pe2Eq");
        criteria.put("graphCode", "Property2Mech");
        dmpRequest.setCriteria(criteria);
        dmpRequest.setSize(voMap.size() * 1L);
        dmpRequest.setPage(1L);
        dmpRequest.setWithoutCount(true);
        // 转换参数
        JSONObject paraRel = null;
        try {
            String paraStr = objectMapper.writeValueAsString(dmpRequest);
            paraRel = JSONObject.parseObject(paraStr);
        } catch (JsonProcessingException e) {
            log.error(e.getMessage(), e);
            return null;
        }
        return rwdClient.queryRelation(context, dmpRequest);
    }

    private DmpResult<JSONArray> queryObjectById(InstanceUrlParam context, Map<String, AdmEquipment> peMap){
        QueryCriteria criteriaObject = new QueryCriteria();
        ObjectNode criteriaPe = JsonNodeFactory.instance.objectNode();
        ArrayNode array = criteriaPe.putObject("id").putArray("$in");
        peMap.keySet().forEach(equipId -> array.add(equipId));
        criteriaPe.put("classCode", "property");
        criteriaPe.put("objType", "property");
        criteriaObject.setCriteria(criteriaPe);
        criteriaObject.setSize(peMap.size() * 1L);
        criteriaObject.setPage(1L);
        criteriaObject.setWithoutCount(true);
        // 转换参数
        JSONObject paraPe = null;
        try {
            String paraStr = objectMapper.writeValueAsString(criteriaObject);
            paraPe = JSONObject.parseObject(paraStr);
        } catch (JsonProcessingException e) {
            log.error(e.getMessage(), e);
            return null;
        }
        return rwdClient.queryObject(context, paraPe);
    }

    private AdmProperty createPropertyObj(String id, String equipId, Boolean isBimIdNull){
        AdmProperty admProperty = new AdmProperty();
        admProperty.setId(id);
        admProperty.setEquipId(equipId);
        if(isBimIdNull){
            admProperty.setBimId(null);
        }
        return admProperty;
    }

    private ObjectNode creatRelationObj(String objTo,String objFrom){
        ObjectNode relationNew = JsonNodeFactory.instance.objectNode();
        relationNew.put("relCode", "Pe2Eq");
        relationNew.put("graphCode", "Property2Mech");
        relationNew.put("graphCode", "Property2Mech001");
        relationNew.put("objTo", objTo);
        relationNew.put("objFrom", objFrom);
        return relationNew;
    }


    /**
     * 更新设备关联的资产（资产中的equipId 信息点） 以及设备资产关系
     * @return
     */
    private Boolean updateRelation2Pe(InstanceUrlParam context, DmpResult<List<ObjRelationDTO>> relationRes, Map<String, AdmEquipment> voMap){
        boolean isDealDataSucc = false;
        List<AdmProperty> updatePropertyList = new ArrayList<AdmProperty>();
        List<String> del_relationIds = new ArrayList<>(relationRes.getData().size());
        Map<String, ObjRelationDTO> relationMap = CollUtil.fieldValueMap(relationRes.getData(), "objTo");
        relationMap.keySet().forEach(euipId -> {
            //存在绑定的资产
            if(voMap.keySet().contains(euipId)){
                AdmEquipment admEquipment = voMap.get(euipId);
                ObjRelationDTO relation = relationMap.get(euipId);
                String propertyId = relation.getObjFrom();
                if(StrUtil.isNotEmpty(admEquipment.getPropertyId()) && StrUtil.isNotEmpty(propertyId)
                        && !admEquipment.getPropertyId().equals(propertyId)){
                    //1 与设备提供的资产绑定
                    AdmProperty propertyFrom = createPropertyObj(admEquipment.getPropertyId(), euipId, false);
                    propertyFrom.setId(admEquipment.getPropertyId());
                    propertyFrom.setEquipId(euipId);
                    updatePropertyList.add(propertyFrom);

                    //2 从关系中查询出的资产与当前设备解绑
                    //更新资产中的信息点
                    AdmProperty property = createPropertyObj(propertyId, null, true);
                    updatePropertyList.add(property);

                    //更新关系
                    del_relationIds.add(relation.getId());
                }
            }
        });
        DigitalRelationFacade.delete(context.getGroupCode(),context.getProjectId(), AdmCommonConstant.APP_ID,null,del_relationIds);
        DmpResult propertyResult = rwdClient.updateObject(context, JSONArray.parseArray(updatePropertyList.toString()));
        if(propertyResult.getResult().equals(DmpResult.SUCCESS)){
            isDealDataSucc = true;
        }
        return isDealDataSucc;
    }


    /**
     * 不存在关系,需要新绑定关系，更新per,插入关系
     * @param context
     * @param voList
     * @param voMap
     * @return
     */
    private Boolean insertRelation2Pe(InstanceUrlParam context, List<AdmEquipment> voList, Map<String, AdmEquipment> voMap){
        boolean isDealDataSucc = false;
        Map<String, AdmEquipment> peMap = CollUtil.fieldValueMap(voList, "propertyId");
        //查询资产
        DmpResult<JSONArray> resultPe = queryObjectById(context, peMap);
        if (CollUtil.isNotEmpty(resultPe.getData())) {
            //更新资产 创建资产设备关系
            List<AdmProperty> propertyList = AdmEntityTransferUtil.toAdmMultiEntity(resultPe.getData(), null, AdmProperty.class);
            List<ObjectNode> objRelationDTONews = new ArrayList<>();
            propertyList.forEach(property -> {
                //存在绑定的资产 更新
                AdmEquipment admEquipment = peMap.get(property.getId());
                property.setEquipId(admEquipment.getId());
                //创建新的设备资产关系
                ObjectNode relationNew = creatRelationObj( admEquipment.getId(), property.getId());
                objRelationDTONews.add(relationNew);
            });

            List<ObjectRelation> relationList = DigitalRelationFacade.create(context.getGroupCode(),context.getProjectId(), AdmCommonConstant.APP_ID,null,objRelationDTONews);
            if(CollUtil.isNotEmpty(relationList)){
                DmpResult<JSONArray> propertyRes = rwdClient.updateObject(context, JSONArray.parseArray(propertyList.toString()));
                if(propertyRes.getResult().equals(DmpResult.SUCCESS)){
                    isDealDataSucc = true;
                }
            }
        }
        return isDealDataSucc;
    }

    /**
     * 查询对象设备类型
     * @param context
     * @param request
     * @param clazz
     * @return
     */
    @Override
    public AdmResponse queryEquipClassCode(InstanceUrlParam context,AdmQueryCriteria request,Class<AdmEquipment> clazz){
        return queryObjectClassCode(context,request,clazz);
    }

    @Override
    public void processRelationType(List<AdmEquipment> vos) {
       /* admRelCalService.calcRelation(AdmContextUtil.toDmpContext(),
                CollUtil.newArrayList(
                        AdmRelationTypeEnum.PE2BD,
                        AdmRelationTypeEnum.PE2FL,
                        AdmRelationTypeEnum.PE2SH,
                        AdmRelationTypeEnum.PE2SP),null);*/
    }
}
