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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.persagy.dmp.basic.model.QueryCriteria;
import com.persagy.dmp.common.constant.CommonConstant;
import com.persagy.dmp.common.context.AppContext;
import com.persagy.dmp.common.helper.SpringHelper;
import com.persagy.dmp.common.model.response.CommonResult;
import com.persagy.dmp.digital.client.DigitalRelationProjectCalFacade;
import com.persagy.dmp.digital.entity.ObjectRelation;
import com.persagy.proxy.adm.constant.AdmRelationTypeEnum;
import com.persagy.proxy.adm.constant.GraphCodeEnum;
import com.persagy.proxy.adm.constant.RelCodeEnum;
import com.persagy.proxy.adm.request.AdmQueryCriteria;
import com.persagy.proxy.adm.request.AdmResponse;
import com.persagy.proxy.adm.service.impl.AbstractAdmBaseServiceImpl;
import com.persagy.proxy.adm.utils.AdmContextUtil;
import com.persagy.proxy.calculation.service.AdmRelCalService;
import com.persagy.proxy.common.entity.DmpResult;
import com.persagy.proxy.common.entity.InstanceUrlParam;
import com.persagy.proxy.object.model.AdmFloor;
import com.persagy.proxy.object.model.AdmSpace;
import com.persagy.proxy.object.service.IAdmFloorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

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

/**
 * ADM 楼层 实现类
 * @author lvxy
 * @date 2021/8/16
 */
@Service
public class AdmFloorServiceImpl extends AbstractAdmBaseServiceImpl<AdmFloor> implements IAdmFloorService {
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private AdmRelCalService admRelCalService;

    public static final Boolean QUERY = true;
    public static final Boolean CREAT_UPDATE = false;
    /***
     * Description: 根据条件查询
     * @param criteria : 请求参数
     * @return : com.persagy.proxy.adm.request.AdmResponse
     * @author : lijie
     * @date :2021/10/28 20:13
     * Update By lijie 2021/10/28 20:13
     */
    @Override
    public AdmResponse query(AdmQueryCriteria criteria) {
        // 1.查询楼层
        InstanceUrlParam context = AdmContextUtil.toDmpContext();
        criteria.setName(AdmFloor.OBJ_TYPE);
        // 设置级联查询参数
        AdmQueryCriteria buildCriteria = new AdmQueryCriteria();
        buildCriteria.setName("building");
        if (CollUtil.isEmpty(criteria.getCascade())){
            criteria.setCascade(CollUtil.newArrayList(buildCriteria));
        }else {
            criteria.getCascade().add(buildCriteria);
        }
        AdmResponse response = this.doQuery(context, criteria, AdmFloor.class);
        List<AdmFloor> admFloors = (List<AdmFloor>) response.getContent();
        if (CollUtil.isNotEmpty(admFloors)){
            admFloors.forEach(admFloor -> {
                if (null==admFloor.getBuilding()){
                    return;
                }
                admFloor.setBuildingId(admFloor.getBuilding().getId());
            });
        }
        // 处理properteies 特殊属性
        ensureSpecialValues(context, admFloors, QUERY);
        return response;
    }
    /**
     * 关系计算处理
     * @param context: 上下文数据,因方法为异步方法,ThreadLocal无法多线程之间共享变量.暂时使用Spring的异步
     * @param vos: 楼层数据
     */
    @Async
    @Override
    public void processRelationType(InstanceUrlParam context,List<AdmFloor> vos) {
        /**
         isttype.add("sh2bd")
         listtype.add("bd2sp")
         listtype.add("fl2sp")
         listtype.add("syeq2bd")
         listtype.add("sy2fl")
         listtype.add("eq2fl")
         listtype.add("pe2bd")
         listtype.add("pe2fl")
         */
        // 2021年12月15日12:17:10 by lijie
        // 改为原有逻辑:仅更新计算标记,不需要计算
        // 1.根据关系枚举查询对应项目的关系计算数据
        List<AdmRelationTypeEnum> admRelationTypeEnums = CollUtil.newArrayList(AdmRelationTypeEnum.SH2BD,
                AdmRelationTypeEnum.BD2SP,
                AdmRelationTypeEnum.FL2SP,
                AdmRelationTypeEnum.SYEQ2BD,
                AdmRelationTypeEnum.SY2FL,
                AdmRelationTypeEnum.EQ2FL,
                AdmRelationTypeEnum.PE2BD,
                AdmRelationTypeEnum.PE2FL);
        Set<String> graphCodes = admRelationTypeEnums.stream()
                .filter(admRelationTypeEnum -> StrUtil.isNotBlank(admRelationTypeEnum.getGraphCode()))
                .map(AdmRelationTypeEnum::getGraphCode)
                .collect(Collectors.toSet());
        Set<String> relCodes = admRelationTypeEnums.stream()
                .filter(admRelationTypeEnum -> StrUtil.isNotBlank(admRelationTypeEnum.getRelCode()))
                .map(AdmRelationTypeEnum::getRelCode)
                .collect(Collectors.toSet());
        QueryCriteria queryCriteria = new QueryCriteria();
        ObjectMapper objectMapper = SpringHelper.getBean(ObjectMapper.class);
        ObjectNode criteria = objectMapper.createObjectNode();
        criteria.putPOJO(ObjectRelation.GRAPH_CODE_HUM,graphCodes);
        criteria.putPOJO(ObjectRelation.REL_CODE_HUM,relCodes);
        criteria.put(ObjectRelation.PROP_VALID,Boolean.TRUE);
        queryCriteria.setCriteria(criteria);
        CommonResult<List<ObjectNode>> commonResult = DigitalRelationProjectCalFacade
                .queryPrototype(AppContext.getContext().getGroupCode(),
                        AppContext.getContext().getProjectId(), AppContext.getContext().getAppId(),
                        AppContext.getContext().getAccountId(), queryCriteria);
        if (!CommonConstant.QUERY_SUCCESS.equals(commonResult.getResult())
                || CollUtil.isEmpty(commonResult.getData())){
            return;
        }
        List<ObjectNode> updateDatas = CollUtil.newArrayList();
        // 2.过滤及处理数据
        Set<String> graphRelSet = new HashSet<>();
        for (AdmRelationTypeEnum admRelationTypeEnum : admRelationTypeEnums) {
            graphRelSet.add(admRelationTypeEnum.getGraphCode()+StrUtil.UNDERLINE+admRelationTypeEnum.getRelCode());
        }
        for (ObjectNode objectNode : commonResult.getData()) {
            if (!objectNode.has(ObjectRelation.GRAPH_CODE_HUM)
                    || !objectNode.get(ObjectRelation.GRAPH_CODE_HUM).isTextual()
                    || StrUtil.isBlank(objectNode.get(ObjectRelation.GRAPH_CODE_HUM).asText())
                    || !objectNode.has(ObjectRelation.REL_CODE_HUM)
                    || !objectNode.get(ObjectRelation.REL_CODE_HUM).isTextual()
                    || StrUtil.isBlank(objectNode.get(ObjectRelation.REL_CODE_HUM).asText())){
                continue;
            }
            String key = objectNode.get(ObjectRelation.GRAPH_CODE_HUM).asText()+StrUtil.UNDERLINE
                    +objectNode.get(ObjectRelation.REL_CODE_HUM).asText();
            if (!graphRelSet.contains(key)){
                continue;
            }
            objectNode.put("computationalState","2");
            updateDatas.add(objectNode);
        }
        if (CollUtil.isNotEmpty(updateDatas)){
            // 3.批量更新数据
            DigitalRelationProjectCalFacade.update(AppContext.getContext().getGroupCode(),
                    AppContext.getContext().getProjectId(), AppContext.getContext().getAppId(),
                    AppContext.getContext().getAccountId(),updateDatas);
        }
    }

    /***
     * Description: 子类重写,以便处理带buildingId
     * @param dmpRequest : 中台查询参数
     * @return : void
     * @author : lijie
     * @date :2021/10/28 20:25
     * Update By lijie 2021/10/28 20:25
     */
    @Override
    protected void processDmpCriteria(QueryCriteria dmpRequest) {
        ObjectNode criteria = dmpRequest.getCriteria();
        if (null==criteria){
            return;
        }
        if (criteria.has("buildingId")){
            // 有建筑id过滤
            ObjectMapper objectMapper = SpringHelper.getBean(ObjectMapper.class);
            ObjectNode relationTo = objectMapper.createObjectNode();
            relationTo.put("graphCode", GraphCodeEnum.ArchSubset.name());
            relationTo.put("relCode", RelCodeEnum.Bd2Fl.name());
            relationTo.set("objFrom",criteria.get("buildingId"));
            criteria.set("relationTo",relationTo);
            criteria.remove("buildingId");
        }
    }

    @Override
    public AdmResponse doQueryOutLine(InstanceUrlParam context, AdmQueryCriteria request, Class<AdmFloor> clazz) {
        request.setProjection(null);
        List<String> list = new ArrayList<>();
        list.add("id");
        list.add("name");
        list.add("localId");
        list.add("localName");
        list.add("classCode");
        list.add("outline");
        list.add("floorSequenceId");
        list.add("modelId");
        list.add("floorElevation");
        list.add("floorMap");
        list.add("floorSequenceID");
        list.add("height");
        list.add("infos");
        list.add("properties");
        request.setProjection(list);
        AdmResponse rs = query(request);
        List<AdmFloor> vos = (List<AdmFloor>) rs.getContent();
        ensureSpecialValues(context, vos, QUERY);
        vos.forEach(vo->{
            Map<String, Object> objectMap = MapUtil.defaultIfEmpty(vo.getInfos(), new HashMap<>());
            objectMap.put("floorSequenceID",vo.getFloorSequenceID());
            vo.setFloorSequenceID(null);
            vo.setInfos(objectMap);
        });
        rs.setContent(vos);
        return rs;
    }

    @Override
    public List<AdmFloor> queryFloorInBuilding(InstanceUrlParam context, String buildingId) {
        AdmQueryCriteria request = new AdmQueryCriteria();
        request.setName(AdmFloor.OBJ_TYPE);
        request.resetRelationCond(false, "ArchSubset", "Bd2Fl", buildingId);
        request.setOrders("floorSequenceID DESC");
        AdmResponse rs = query(request);
        List<AdmFloor> vos = (List<AdmFloor>) rs.getContent();
        ensureSpecialValues(context, vos, QUERY);
        return vos;
    }

    /**
     * 楼层自定义信息点处理 新增，properties 转化为String类型，放到propertiesInfo信息点存储，查询相关，propertiesInfo转为为properties 页面展示
     * @param context
     * @param queryFlag true 查询，false 更新或者新增
     * @return
     */
    @Override
    public void ensureSpecialValues(InstanceUrlParam context, List<AdmFloor> vos, boolean queryFlag) {
        if(CollUtil.isNotEmpty(vos)){
            if(queryFlag){//查询
                vos.stream().forEach(admFloor -> {
                    if(StrUtil.isNotEmpty(admFloor.getPropertiesInfo())){
                        try {
                            ObjectNode properties = (ObjectNode) objectMapper.readTree(admFloor.getPropertiesInfo());
                            admFloor.setProperties(properties);
                            admFloor.setPropertiesInfo(null);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }else{//新增或者更新
                vos.stream().forEach(admFloor -> {
                    if(!JSONUtil.isNull(admFloor.getProperties())){
                        //admFloor.setPropertiesInfo(admFloor.getProperties().toString());
                        admFloor.setPropertiesInfo(admFloor.getProperties().toString());
                        admFloor.setProperties(null);
                    }
                });
            }
        }
    }


    @Override
    protected void processResultAfterQuery(DmpResult<JSONArray> response) {
        // 2021年11月3日17:39:28 by lijie.特殊处理一下返回的properties信息点,不然转换为对象时会报转换异常
        JSONArray data = new JSONArray();
        List array = response.getData();
        if (null!=array && array.size()>0){
            data.addAll(array);
            int size = data.size();
            for (int i = 0; i < size; i++) {
                JSONObject temp = data.getJSONObject(i);
                if (temp.containsKey("infos") && temp.getJSONObject("infos").containsKey("properties")
                        && StrUtil.isNotBlank(temp.getJSONObject("infos").getString("properties"))
                        && temp.getJSONObject("infos").getString("properties").startsWith("{")){
                    temp.getJSONObject("infos")
                            .put("properties",JSONObject.parseObject(temp.getJSONObject("infos").getString("properties")));
                }
                if (temp.containsKey("properties")
                        && StrUtil.isNotBlank(temp.getString("properties"))
                        && temp.getString("properties").startsWith("{")){
                    temp.put("properties",JSONObject.parseObject(temp.getString("properties")));
                }
                data.set(i,temp);
            }
        }
        response.setData(data);
    }
}