package com.persagy.dmp.rwd.digital.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.persagy.dmp.common.constant.ValidEnum;
import com.persagy.dmp.common.context.AppContext;
import com.persagy.dmp.common.model.entity.BaseEntity;
import com.persagy.dmp.define.entity.ObjectInfoDefine;
import com.persagy.dmp.rwd.basic.constant.DigitalMessageConstant;
import com.persagy.dmp.rwd.basic.utils.DigitalCommonUtils;
import com.persagy.dmp.rwd.basic.utils.DigitalMessageHelper;
import com.persagy.dmp.rwd.define.service.IObjectInfoService;
import com.persagy.dmp.rwd.define.service.IObjectTypeService;
import com.persagy.dmp.rwd.digital.dao.ObjectDigitalMapper;
import com.persagy.dmp.rwd.digital.entity.ObjectDigital;
import com.persagy.dmp.rwd.digital.service.IObjectDigitalService;
import com.persagy.dmp.rwd.parser.service.InfoDataFactory;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
 * 对象数据 实现类
 * @author Charlie Yu
 * @date 2021-07-08
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class ObjectDigitalServiceImpl implements IObjectDigitalService {

    @Resource
    private ObjectDigitalMapper dao;
    @Autowired
    private DigitalMessageHelper messageSender;
    @Autowired
    private IObjectInfoService infoService;
    @Autowired
    private IObjectTypeService typeService;

    @Override
    public ObjectDigital load(String id) {
        return dao.selectById(id);
    }

    @Override
    public List<ObjectDigital> queryByCondition(Wrapper<ObjectDigital> queryWrapper) {
        return dao.selectList(queryWrapper);
    }

    @Override
    public Integer queryCount(Wrapper<ObjectDigital> queryWrapper) {
        return dao.selectCount(queryWrapper);
    }

    @Override
    public Page queryByCondition(Page page, Wrapper<ObjectDigital> queryWrapper) {
        return dao.selectPage(page, queryWrapper);
    }

    @Override
    public List<ObjectDigital> insert(List<ObjectDigital> voList) {
        if(CollUtil.isEmpty(voList)) {
            return null;
        }
        validateOnSave(voList, false);
        //获取对象类型
        Map<String, String> typeMap = typeService.queryObjTypeMap();
        voList.forEach(vo -> {
            // 生成ID
            vo.setId(DigitalCommonUtils.fetchObjectId(vo.getProjectId(), vo.getObjType()));
            //设置项目id
            vo.setProjectId(AppContext.getContext().getProjectId());
            //设置集团编码
            vo.setGroupCode(AppContext.getContext().getGroupCode());
            //设置对象类型-objType
            vo.setObjType(typeMap.get(vo.getClassCode()));
            vo.setValid(ValidEnum.TRUE.getType());
            // 新增
            dao.insert(vo);
            // 新增后消息
            messageSender.sendMessage(DigitalMessageConstant.OPERATE_AFTER_INSERT, null, vo, false);
        });
        return voList;
    }


    @Override
    public List<ObjectDigital> update(List<ObjectDigital> voList){
        if(CollUtil.isEmpty(voList)) {
            return null;
        }
        //获取$remove字段
        Map<String, Object> removeMap = getRemoveInfo(voList);
        //获取基础属性
        Set<String> fieldSet = ReflectUtil.getFieldMap(ObjectDigital.class).keySet();
        //查询数据库中存在的数据
        List<String> idList = CollUtil.getFieldValues(voList, BaseEntity.PROP_ID, String.class);
        List<ObjectDigital> dbList = dao.selectBatchIds(idList);
        Map<String, ObjectDigital> dbMap = CollectionUtil.fieldValueMap(dbList, BaseEntity.PROP_ID);

        //填充classCode
        fillClassCodeForUpdate(voList,dbMap);
        //信息点校验
        Map<String, Map<String, ObjectInfoDefine>> allInfoMap = validateOnSave(voList, true);
        List<ObjectDigital> resultList = new ArrayList<>();
        voList.forEach(vo -> {
            ObjectDigital dbVO = MapUtil.get(dbMap, vo.getId(), ObjectDigital.class);
            // 容错处理
            if(dbVO == null) {
                return;
            }

            //数据合并
            ObjectDigital newDbVO = dbVO;
            Map<String, ObjectInfoDefine> infoDefineMap = allInfoMap.get(vo.getClassCode());
            dataMerge(newDbVO,vo,infoDefineMap,fieldSet);

            //移除需要删除的信息点
            Object remove = removeMap.get(newDbVO.getId());
            remove(newDbVO,remove,fieldSet);

            // 修改
            dao.updateById(newDbVO);
            resultList.add(newDbVO);
            // 修改后消息
            messageSender.sendMessage(DigitalMessageConstant.OPERATE_AFTER_UPDATE, dbVO, newDbVO, false);
        });
        return resultList;
    }

    @Override
    public void delete(List<String> idList) {
        if(CollUtil.isEmpty(idList)) {
            return;
        }
        List<ObjectDigital> dbList = dao.selectBatchIds(idList);
        Map<String, ObjectDigital> dbMap = CollectionUtil.fieldValueMap(dbList, BaseEntity.PROP_ID);
        idList.forEach(id -> {
            ObjectDigital dbVO = MapUtil.get(dbMap, id, ObjectDigital.class);
            // 容错处理
            if(dbVO == null) {
                return;
            }
            // 删除
            ObjectDigital vo = new ObjectDigital();
            vo.setId(id);
            vo.setValid(ValidEnum.FALSE.getType());
            dao.updateById(vo);
            // 删除后消息
            dbVO.setValid(ValidEnum.FALSE.getType());
            messageSender.sendMessage(DigitalMessageConstant.OPERATE_AFTER_DELETE, null, dbVO, false);
        });
    }

    /**
     * 保存校验
     * 编码全局唯一
     * @param voList
     * @param isUpdate
     */
    private Map<String, Map<String, ObjectInfoDefine>> validateOnSave(List<ObjectDigital> voList, boolean isUpdate) {
        return validateInfoCode(voList);
    }

    /**
     * 信息点值校验
     * @param voList
     */
    private Map<String, Map<String, ObjectInfoDefine>> validateInfoCode(List<ObjectDigital> voList) {
        //获取该信息点当前classCode
        List<String> typeCodeList = CollUtil.getFieldValues(voList, "classCode", String.class);

        //查询通用类型：equipment,building,floor,space等
        Map<String, String> objTypeMap = typeService.queryObjTypeMap();
        queryParentClassCode(typeCodeList,objTypeMap);

        // 查询信息点定义
        Map<String, Map<String, ObjectInfoDefine>> allInfoMap = infoService.queryByTypes(typeCodeList);
        // 信息点校验器
        for(ObjectDigital vo:voList) {
            Map<String, ObjectInfoDefine> infoMap = allInfoMap.get(vo.getClassCode());
            String objType = objTypeMap.get(vo.getClassCode());
            //设置通用信息点
            if(!objType.equals(vo.getClassCode())){
                Map<String, ObjectInfoDefine> commonInfoDefineMap = allInfoMap.get(objType);
                if(commonInfoDefineMap!=null || commonInfoDefineMap.size()>0){
                    infoMap.putAll(commonInfoDefineMap);
                }
            }
            if(MapUtil.isEmpty(infoMap)) {
                continue;
            }
            // 信息点值对象
            ObjectNode infos = vo.getInfos();
            if(infos == null || infos.isNull()) {
                continue;
            }
            // 按infos属性循环处理
            Iterator<String> infoKeys = infos.fieldNames();
            while(infoKeys.hasNext()) {
                String infoCode = infoKeys.next();
                // 如果信息点中不存在，移除
                if(!infoMap.containsKey(infoCode)) {
                    // infos.remove(infoCode);
                    infoKeys.remove();
                    continue;
                }
                // 校验信息点值
                InfoDataFactory.parse(infoMap.get(infoCode), infos.get(infoCode));

            }
        }
        return allInfoMap;
    }

    /**
     * 获取remove信息点
     * @param voList
     * @return
     */
    private Map<String, Object> getRemoveInfo(List<ObjectDigital> voList){
        Map<String, Object> removeMap = new HashMap<>();
        for (ObjectDigital digital : voList) {
            ObjectNode infos = digital.getInfos();
            if(infos!=null){
                removeMap.put(digital.getId(),infos.get("$remove"));
            }
        }
        return removeMap;
    }

    /**
     * 查询对应的通用类型
     * @param typeCodeList
     */
    private void  queryParentClassCode(List<String> typeCodeList,Map<String, String> objTypeMap){
        if(typeCodeList == null || typeCodeList.size()<=0){
            return ;
        }
        //查询classCode对应点objType
        List<String> parentCodes = new ArrayList<>();
        for (String classCode : typeCodeList) {
            String objType = objTypeMap.get(classCode);
            if(StringUtils.isEmpty(objType) || "0".equals(objType) || parentCodes.contains(objType)){
                return ;
            }
            parentCodes.add(objType);
        }

        if(parentCodes!=null && parentCodes.size()>0){
            typeCodeList.addAll(parentCodes);
        }
        return ;
    }
    /**
     * 数据合并
     * @param dbVO 源数据
     * @param vo 目标数据
     * @param vo 待删除待数据
     */
    private void dataMerge(ObjectDigital dbVO,ObjectDigital vo,Map<String, ObjectInfoDefine> infoDefineMap,Set<String> fieldSet){
        //基础属性处理
        handleBasicColumn(dbVO,vo);
        //动态信息点处理
        ObjectNode infos = vo.getInfos();
        for (Map.Entry<String, ObjectInfoDefine> entry : infoDefineMap.entrySet()) {
            String code = entry.getKey();
            if(fieldSet.contains(code)){
                //属于基础信息点
                continue;
            }
            if (!infos.has(code)) {
                continue;
            }
            JsonNode value = infos.get(code);
            if (dbVO.getInfos() == null) {
                dbVO.setInfos(JsonNodeFactory.instance.objectNode());
            }
            dbVO.getInfos().set(code, value);
        }
    }

    /**
     * 基础属性处理
     * @param dbVO
     * @param vo
     */
    private void handleBasicColumn(ObjectDigital dbVO,ObjectDigital vo){
        if(StringUtils.isNotEmpty(vo.getName())){
            dbVO.setName(vo.getName());
        }
        if(StringUtils.isNotEmpty(vo.getLocalId())){
            dbVO.setLocalId(vo.getLocalId());
        }
        if(StringUtils.isNotEmpty(vo.getLocalName())){
            dbVO.setLocalName(vo.getLocalName());
        }
        if(vo.getVirtualCodes()!=null){
            dbVO.setVirtualCodes(vo.getVirtualCodes());
        }
        if(vo.getValid()!=null){
            dbVO.setValid(vo.getValid());
        }
    }


    /**
     * 删除需要移除的信息点
     * @param dbVO
     * @param remove
     * @param BasicfieldSet
     */
    private void remove(ObjectDigital dbVO,Object remove,Set<String> BasicfieldSet){
        if(remove == null){
            return;
        }
        JSONArray array = JSONArray.parseArray(remove.toString());
        array.forEach(item->{
            String removeCode = (String) item;
            if(BasicfieldSet.contains(removeCode)){
                //基础信息
                if("name".equals(removeCode)){
                    dbVO.setName(null);
                }
                if("localId".equals(removeCode)){
                    dbVO.setLocalId(null);
                }
                if("localName".equals(removeCode)){
                    dbVO.setLocalName(null);
                }
                if("virtualCodes".equals(removeCode)){
                    dbVO.setVirtualCodes(new ArrayList<>());
                }
                return;
            }
            dbVO.getInfos().remove(removeCode);
        });
    }

    /**
     * 修改时填充classCode
     * @param voList
     * @param dbMap
     */
    private void fillClassCodeForUpdate(List<ObjectDigital> voList,Map<String, ObjectDigital> dbMap){
        if(voList == null || voList.size()<=0){
            return;
        }
        for (ObjectDigital digital : voList) {
            ObjectDigital dbDigital = dbMap.get(digital.getId());
            if(dbDigital!=null){
                digital.setClassCode(dbDigital.getClassCode());
            }
        }
    }


    /**
     * 查询虚拟信息点
     * @param projectId
     * @param groupCode
     * @param ids
     * @return
     */
    @Override
    public Map<String, List<String>> queryVirtualCodeMap(String projectId,String groupCode,List<String> ids) {
        LambdaQueryWrapper<ObjectDigital> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(ObjectDigital::getId, ObjectDigital::getVirtualCodes);
        wrapper.eq(ObjectDigital::getProjectId,projectId);
        wrapper.eq(ObjectDigital::getGroupCode,groupCode);
        wrapper.in(ObjectDigital::getId,ids);
        wrapper.eq(ObjectDigital::getValid, ValidEnum.TRUE.getType());
        List<ObjectDigital> objectDigitals = queryByCondition(wrapper);
        return CollUtil.fieldValueAsMap(objectDigitals, "id", "virtual_codes");
    }

}
