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.common.constant.ValidEnum;
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.define.entity.ObjectTypeDefine;
import com.persagy.dmp.mybatis.utils.ConditionUtil;
import com.persagy.dmp.rwd.basic.constant.DigitalMessageConstant;
import com.persagy.dmp.rwd.basic.utils.DigitalMessageHelper;
import com.persagy.dmp.rwd.define.dao.ObjectTypeMapper;
import com.persagy.dmp.rwd.define.service.IObjectTypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 对象类型 实现类
 * @author Charlie Yu
 * @date 2021-06-25
 */
@Service
@Transactional(rollbackFor = Exception.class)
@CacheConfig(cacheNames = {"dt_object_type"})
public class ObjectTypeServiceImpl implements IObjectTypeService {

    @Resource
    private ObjectTypeMapper dao;
    @Autowired
    private DigitalMessageHelper messageSender;

    @Override
    @Cacheable(key = "targetClass + methodName + #p0 + #p1")
    public List<ObjectTypeDefine> queryByProjectAndApp(String projectId, String appId) {
        // TODO 指定条件查询使用Redis
        QueryWrapper<ObjectTypeDefine> wrapper = new QueryWrapper<>();
        ConditionUtil.ensureProjectCriteria(wrapper, projectId);
        if(StrUtil.isNotBlank(appId)) {
            // TODO 根据应用ID关联订阅条件

        }
        return queryByCondition(wrapper);
    }

    @Override
    @Cacheable(key = "targetClass + methodName")
    public Map<String, String> queryCodeNameMap() {
        LambdaQueryWrapper<ObjectTypeDefine> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(ObjectTypeDefine::getId, ObjectTypeDefine::getCode, ObjectTypeDefine::getName);
        wrapper.eq(ObjectTypeDefine::getValid, ValidEnum.TRUE.getType());
        List<ObjectTypeDefine> voList = queryByCondition(wrapper);
        return CollUtil.fieldValueAsMap(voList, "code", "name");
    }

    @Override
    @Cacheable(key = "targetClass + methodName")
    public Map<String, String> queryObjTypeMap() {
        LambdaQueryWrapper<ObjectTypeDefine> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(ObjectTypeDefine::getId, ObjectTypeDefine::getCode, ObjectTypeDefine::getObjType);
        wrapper.eq(ObjectTypeDefine::getValid, ValidEnum.TRUE.getType());
        List<ObjectTypeDefine> voList = queryByCondition(wrapper);
        return CollUtil.fieldValueAsMap(voList, "code", "objType");
    }

    @Override
    public Page queryByProjectAndApp(Page page, String projectId, String appId) {
        // TODO 分页查询使用ES
        QueryWrapper<ObjectTypeDefine> wrapper = new QueryWrapper<>();
        ConditionUtil.ensureProjectCriteria(wrapper, projectId);
        if(StrUtil.isNotBlank(appId)) {
            // TODO 根据应用ID关联订阅条件

        }
        return queryByCondition(page, wrapper);
    }

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

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

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

    @Override
    @CacheEvict(allEntries = true)
    public List<ObjectTypeDefine> insert(List<ObjectTypeDefine> 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
    @CacheEvict(allEntries = true)
    public List<ObjectTypeDefine> update(List<ObjectTypeDefine> voList) {
        if(CollUtil.isEmpty(voList)) {
            return null;
        }
        validateOnSave(voList, true);
        List<String> idList = CollUtil.getFieldValues(voList, BaseEntity.PROP_ID, String.class);
        List<ObjectTypeDefine> dbList = dao.selectBatchIds(idList);
        Map<String, ObjectTypeDefine> dbMap = CollectionUtil.fieldValueMap(dbList, BaseEntity.PROP_ID);
        voList.forEach(vo -> {
            ObjectTypeDefine dbVO = MapUtil.get(dbMap, vo.getId(), ObjectTypeDefine.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<ObjectTypeDefine> voList, boolean isUpdate) {
        Set<String> codeSet = new HashSet<>();
        List<String> idList = new ArrayList<>();
        for(ObjectTypeDefine vo:voList) {
            // 编码重复
            if(!codeSet.add(vo.getCode())) {
                throw new BusinessException(StrFormatter.format("存在重复编码{}，不允许保存！", vo.getCode()));
            }
            // 修改时，收集待排除的主键
            if(isUpdate) {
                idList.add(vo.getId());
            }
        }
        // 查询条件
        LambdaQueryWrapper<ObjectTypeDefine> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(ObjectTypeDefine::getCode);
        queryWrapper.in(ObjectTypeDefine::getValid, ValidEnum.TRUE.getType());
        queryWrapper.in(ObjectTypeDefine::getCode, codeSet);
        if(isUpdate) {
            queryWrapper.notIn(ObjectTypeDefine::getId, idList);
        }
        // 查询重复的编码
        List<ObjectTypeDefine> 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
    @CacheEvict(allEntries = true)
    public void delete(List<String> idList) {
        if(CollUtil.isEmpty(idList)) {
            return;
        }
        List<ObjectTypeDefine> dbList = dao.selectBatchIds(idList);
        Map<String, ObjectTypeDefine> dbMap = CollectionUtil.fieldValueMap(dbList, BaseEntity.PROP_ID);
        idList.forEach(id -> {
            ObjectTypeDefine dbVO = MapUtil.get(dbMap, id, ObjectTypeDefine.class);
            // 容错处理
            if(dbVO == null) {
                return;
            }
            // 删除前消息 - 暂不提供同步消息
///            messageSender.sendMessage(DigitalMessageConstant.OPERATE_BEFORE_DELETE, null, dbVO, true);
            // 删除
            ObjectTypeDefine vo = new ObjectTypeDefine();
            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);
        });
    }
}
