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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.persagy.dmp.basic.constant.DigitalMessageConstant;
import com.persagy.dmp.common.constant.CommonConstant;
import com.persagy.dmp.common.constant.ValidEnum;
import com.persagy.dmp.common.context.AppContext;
import com.persagy.dmp.common.exception.BusinessException;
import com.persagy.dmp.common.model.entity.BaseEntity;
import com.persagy.dmp.define.entity.ObjectInfoDefine;
import com.persagy.dmp.rwd.basic.utils.DigitalMessageHelper;
import com.persagy.dmp.rwd.define.dao.ObjectInfoMapper;
import com.persagy.dmp.rwd.define.service.IObjectInfoService;
import com.persagy.dmp.rwd.parser.service.InfoDataFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 信息点 实现类
 * @author Charlie Yu
 * @date 2021-06-25
 */
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class ObjectInfoServiceImpl implements IObjectInfoService {

    private final ObjectInfoMapper dao;
    private final DigitalMessageHelper messageSender;

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

    @Override
    public List<ObjectInfoDefine> queryByIds(List<String> idList) {
        return dao.selectBatchIds(idList);
    }

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

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

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

    @Override
    public List<ObjectInfoDefine> insert(List<ObjectInfoDefine> voList) {
        if(CollUtil.isEmpty(voList)) {
            return null;
        }
        validateOnSave(voList, false);
        voList.forEach(vo -> {
            // 新增前消息 - 暂不提供同步消息
///            messageSender.sendMessage(DigitalMessageConstant.OPERATE_BEFORE_INSERT, null, vo, true);
            // 新增
            dao.insert(vo);
            // 新增后消息
            messageSender.sendMessage(DigitalMessageConstant.OPERATE_AFTER_INSERT, null, vo, false);
        });
        return voList;
    }

    @Override
    public List<ObjectInfoDefine> update(List<ObjectInfoDefine> voList) {
        if(CollUtil.isEmpty(voList)) {
            return null;
        }
        validateOnSave(voList, true);
        List<String> idList = CollUtil.getFieldValues(voList, BaseEntity.PROP_ID, String.class);
        List<ObjectInfoDefine> dbList = dao.selectBatchIds(idList);
        Map<String, ObjectInfoDefine> dbMap = CollectionUtil.fieldValueMap(dbList, BaseEntity.PROP_ID);
        voList.forEach(vo -> {
            ObjectInfoDefine dbVO = MapUtil.get(dbMap, vo.getId(), ObjectInfoDefine.class);
            // 容错处理
            if(dbVO == null) {
                return;
            }
            // 修改前消息 - 暂不提供同步消息
///            messageSender.sendMessage(DigitalMessageConstant.OPERATE_BEFORE_UPDATE, dbVO, vo, true);
            // 修改
            dao.updateById(vo);
            // 修改后消息
            messageSender.sendMessage(DigitalMessageConstant.OPERATE_AFTER_UPDATE, dbVO, vo, false);
        });
        return voList;
    }

    /**
     * 保存校验
     * 编码全局唯一
     * @param voList
     * @param isUpdate
     */
    private void validateOnSave(List<ObjectInfoDefine> voList, boolean isUpdate) {
        Set<String> codeSet = new HashSet<>();
        List<String> idList = new ArrayList<>();
        for(ObjectInfoDefine vo:voList) {
            // 编码重复
            if(!codeSet.add(vo.getCode())) {
                throw new BusinessException(StrFormatter.format("存在重复编码{}，不允许保存！", vo.getCode()));
            }
            // 校验器校验
            InfoDataFactory.parse(vo, null);
            // 修改时，收集待排除的主键
            if(isUpdate) {
                idList.add(vo.getId());
            }
            // 补充valid值
            if (null==vo.getValid()){
                vo.setValid(ValidEnum.TRUE.getType());
            }
        }
        // 查询条件
        LambdaQueryWrapper<ObjectInfoDefine> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(ObjectInfoDefine::getCode);
        queryWrapper.in(ObjectInfoDefine::getValid, ValidEnum.TRUE.getType());
        queryWrapper.in(ObjectInfoDefine::getCode, codeSet);
        if(isUpdate) {
            queryWrapper.notIn(ObjectInfoDefine::getId, idList);
        }
        // 查询重复的编码
        List<ObjectInfoDefine> dbList = dao.selectList(queryWrapper);
        if(CollUtil.isEmpty(dbList)) {
            return;
        }
        List<String> codes = CollUtil.getFieldValues(voList, "code", String.class);
        throw new BusinessException(StrFormatter.format("存在重复编码{}，不允许保存！", codes));
    }

    @Override
    public void delete(List<String> idList) {
        if(CollUtil.isEmpty(idList)) {
            return;
        }
        List<ObjectInfoDefine> dbList = dao.selectBatchIds(idList);
        Map<String, ObjectInfoDefine> dbMap = CollectionUtil.fieldValueMap(dbList, BaseEntity.PROP_ID);
        idList.forEach(id -> {
            ObjectInfoDefine dbVO = MapUtil.get(dbMap, id, ObjectInfoDefine.class);
            // 容错处理
            if(dbVO == null) {
                return;
            }
            // 删除前消息 - 暂不提供同步消息
///            messageSender.sendMessage(DigitalMessageConstant.OPERATE_BEFORE_DELETE, null, dbVO, true);
            // 删除
            ObjectInfoDefine vo = new ObjectInfoDefine();
            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);
        });
    }

    @Override
    public List<ObjectInfoDefine> queryByClassCode(String projectId, String classCode) {
        if(StrUtil.isBlank(classCode)) {
            return null;
        }
        return queryByClassCodes(projectId, CollUtil.newArrayList(classCode));
    }

    @Override
    public List<ObjectInfoDefine> queryByClassCodes(String projectId, Iterable<String> classCodes) {
        return queryByClassCodes(projectId, classCodes, null);
    }

    @Override
    public List<ObjectInfoDefine> queryByClassCodes(String projectId, Iterable<String> classCodes, QueryWrapper<ObjectInfoDefine> wrapper) {
        if(StrUtil.isBlank(projectId)) {
            // 如果项目ID为空，不返回
            return null;
        }
        if(wrapper == null) {
            wrapper = new QueryWrapper<>();
        }
        // 默认查所有字段
        if(StrUtil.isBlank(wrapper.getSqlSelect())) {
            wrapper.select("*");
        }
        List<String> projectIds = CollUtil.newArrayList(CommonConstant.DEFAULT_ID, AppContext.getContext().getGroupCode(), projectId);
        return dao.queryByClassCodes(projectIds, classCodes, wrapper);
    }

    @Override
    public Map<String, Map<String, ObjectInfoDefine>> queryInfoMapByClass(String projectId, Iterable<String> classCodes) {
        return queryInfoMapByClass(projectId, classCodes, null);
    }

    @Override
    public Map<String, Map<String, ObjectInfoDefine>> queryInfoMapByClass(String projectId, Iterable<String> classCodes, QueryWrapper<ObjectInfoDefine> wrapper) {
        List<ObjectInfoDefine> vos = queryByClassCodes(projectId, classCodes, wrapper);
        if(CollUtil.isEmpty(vos)) {
            return null;
        }
        return vos.stream().collect(Collectors.groupingBy(ObjectInfoDefine::getClassCode,
                Collectors.toMap(ObjectInfoDefine::getCode, p->p, (v1, v2) -> v1)));
    }

    @Override
    public Map<String, Long> queryCollectReport(String projectId, QueryWrapper<ObjectInfoDefine> wrapper) {
        if(StrUtil.isBlank(projectId)) {
            return null;
        }
        List<String> projectIds = CollUtil.newArrayList(CommonConstant.DEFAULT_ID, AppContext.getContext().getGroupCode(), projectId);
        wrapper = ensureCollectWrapper(wrapper, projectIds);
        return dao.queryCollectReport(projectIds, null, wrapper);
    }

    @Override
    public List<Map<String, Object>> queryCollectInfos(String projectId, QueryWrapper<ObjectInfoDefine> wrapper) {
        if(StrUtil.isBlank(projectId)) {
            return null;
        }
        List<String> projectIds = CollUtil.newArrayList(CommonConstant.DEFAULT_ID, AppContext.getContext().getGroupCode(), projectId);
        wrapper = ensureCollectWrapper(wrapper, projectIds);
        return dao.queryCollectInfos(projectIds, null, wrapper);
    }

    @Override
    public Page<Map<String, Object>> queryCollectInfos(Page pageable, String projectId, QueryWrapper<ObjectInfoDefine> wrapper) {
        if(StrUtil.isBlank(projectId)) {
            return null;
        }
        List<String> projectIds = CollUtil.newArrayList(CommonConstant.DEFAULT_ID, AppContext.getContext().getGroupCode(), projectId);
        wrapper = ensureCollectWrapper(wrapper, projectIds);
        return dao.queryCollectInfos(pageable, projectIds, null, wrapper);
    }

    /**
     * 补充wrapper信息
     * @param wrapper
     * @param projectIds
     * @return
     */
    private QueryWrapper<ObjectInfoDefine> ensureCollectWrapper(QueryWrapper<ObjectInfoDefine> wrapper, List<String> projectIds) {
        if(wrapper == null) {
            wrapper = new QueryWrapper<>();
        }
        wrapper.in("dt_object.project_id", projectIds.toArray(new String[0]));
        wrapper.orderByAsc("dt_object.local_id", "dt_define_info.class_code", "dt_define_info.code");
        return wrapper;
    }
}
