package com.ho.flow.service.impl;


import com.google.common.collect.Lists;
import com.ho.business.vo.req.deviceModel.SynchronousReqVo;
import com.ho.common.tools.constant.CommonConstant;
import com.ho.common.tools.constant.RedisKeyConstant;
import com.ho.common.tools.entity.SimpleUser;
import com.ho.common.tools.exception.BaseResponseCode;
import com.ho.common.tools.exception.BusinessException;
import com.ho.common.tools.service.RedisService;
import com.ho.common.tools.util.RandomUtil;
import com.ho.flow.constant.FlowConstant;
import com.ho.flow.mapper.AlarmConfigMapper;
import com.ho.flow.mapper.DeviceColDefineMapper;
import com.ho.flow.service.AlarmConfigService;
import com.ho.flow.service.DeviceColDefineService;
import com.ho.flow.vo.AlarmConfig;
import com.ho.flow.vo.DeviceColDefine;
import com.ho.flow.vo.req.AlarmConfig.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * @author gyan
 * @description 针对表【alarm_config】的数据库操作Service实现
 * @createDate 2023-02-01 17:15:01
 */
@Service
@Slf4j
public class AlarmConfigServiceImpl implements AlarmConfigService {

    @Autowired
    AlarmConfigMapper alarmConfigMapper;

    @Autowired
    DeviceColDefineMapper deviceColDefineMapper;

    @Autowired
    DeviceColDefineService deviceColDefineService;

    @Autowired
    RedisService redisService;

    //不分页查询
    @Override
    public List<AlarmConfig> selectList(AlarmConfigPageReqVo vo) {
        List<AlarmConfig> alarmList = alarmConfigMapper.selectList(vo);
        return alarmList;
    }

    @Override
    //@Cacheable(cacheManager = "fiveMinuteCacheManager", value = "AlarmConfigListGroupCache", key = "#groupId+'_'+#scope+'_'+#deviceType", sync = true)
    public List<AlarmConfig> selectListGroupCache(Integer groupId, Integer scope ,String deviceType) {
        List<AlarmConfig> alarmList = null;
        String key = RedisKeyConstant.ALARM.GROUP_ALARM_CONFIG + groupId + ":"+ scope + ":" + deviceType;
        if(redisService.hasKey(key)){
            alarmList =(List<AlarmConfig>) redisService.get(key);
        }else{
            AlarmConfigPageReqVo vo = new AlarmConfigPageReqVo();
            vo.setGroupId(groupId);
            vo.setScope(scope);
            vo.setDeviceType(deviceType);
            alarmList = alarmConfigMapper.selectList(vo);
            redisService.set(key,alarmList);
            redisService.expire(key, RandomUtil.getRandomInt(), TimeUnit.MINUTES);
        }
        return alarmList;
    }

    @Override
    //@Cacheable(cacheManager = "fiveMinuteCacheManager", value = "AlarmConfigListCompanyCache", key = "#groupId+'_'+#stationId+'_'+#scope+'_'+#deviceType", sync = true)
    public List<AlarmConfig> selectListCompanyCache(Integer groupId, Integer stationId, Integer scope ,String deviceType) {
        List<AlarmConfig> alarmList = null;
        String key = RedisKeyConstant.ALARM.COMPANY_ALARM_CONFIG + groupId + ":"+ stationId + ":" + deviceType;
        if(redisService.hasKey(key)){
            alarmList =(List<AlarmConfig>) redisService.get(key);
        }else{
            AlarmConfigPageReqVo vo = new AlarmConfigPageReqVo();
            vo.setGroupId(groupId);
            vo.setStationId(stationId);
            vo.setScope(scope);
            vo.setDeviceType(deviceType);
            alarmList = alarmConfigMapper.selectList(vo);
            redisService.set(key,alarmList);
            redisService.expire(key,RandomUtil.getRandomInt(), TimeUnit.MINUTES);
        }
        return alarmList;
    }

    @Override
    public void add(AlarmConfigAddReqVo vo, SimpleUser user) {
        //根据col_define_id，查询到device_type device_type_id  col col_name sens_type
        //新增DeviceTypeId 查询条件，判断是否存在内置设备
        DeviceColDefine deviceColDefine = deviceColDefineService.selectByIdAndDeviceTypeId(vo.getColDefineId(), vo.getDeviceTypeId());
        if (deviceColDefine == null) {
            //配置元素不存在
            throw new BusinessException(BaseResponseCode.CONFIGURATION_ELEMENT_DOES_NOT_EXIST);
        }
        //查询是否有重复的数据,需要增加集团id, 电站id,并且查询未删除的
        AlarmQuery alarmQuery = new AlarmQuery();
        alarmQuery.setGroupId(user.getGroupId());
        //电站级 查询电站, 集团级别不查询电站
        if (FlowConstant.AlarmConfig.SCOPE_STATION.equals(vo.getScope())) {
            alarmQuery.setStationId(vo.getStationId());
        }
        alarmQuery.setScope(vo.getScope());
        alarmQuery.setColDefineId(vo.getColDefineId());
        List<AlarmConfig> alarmConfigList = alarmConfigMapper.selectRepeatValue(alarmQuery);
        if (!alarmConfigList.isEmpty()) {
            throw new BusinessException(BaseResponseCode.EXISTS_DUPLICATE_DATA);
        }
        AlarmConfig alarmConfig = new AlarmConfig();
        //判断类型是否为  1遥信(一个值) 或者 2遥测(两个值)
        if (CommonConstant.ONE.equals(deviceColDefine.getSensType())) {
            if (vo.getAlarmLevel() == null) {
                throw new BusinessException(BaseResponseCode.TELEMETRY_NOT_IS_NULL);
            } else {
                alarmConfig.setAlarmLevel(vo.getAlarmLevel());
                //把遥信的两个值冗余进来
                alarmConfig.setInfo0(deviceColDefine.getInfo0());
                alarmConfig.setInfo1(deviceColDefine.getInfo1());
            }
        }
        if (CommonConstant.TWO.equals(deviceColDefine.getSensType())) {
            if (vo.getLowerLimit() == null && vo.getUpperLimit() == null) {
                throw new BusinessException(BaseResponseCode.TELEMETRY_VALUE_NOT_LIMIT);
            } else {
                alarmConfig.setUpperLimit(vo.getUpperLimit());
                alarmConfig.setLowerLimit(vo.getLowerLimit());
            }
        }
        //集团id
        alarmConfig.setGroupId(user.getGroupId());
        //电站级别查询电站
        if (FlowConstant.AlarmConfig.SCOPE_STATION.equals(vo.getScope())) {
            alarmConfig.setStationId(vo.getStationId());
        }
        alarmConfig.setScope(vo.getScope());
        alarmConfig.setColDefineId(vo.getColDefineId());
        alarmConfig.setDeviceTypeId(deviceColDefine.getDeviceTypeId());
        alarmConfig.setDeviceType(deviceColDefine.getDeviceType());
        alarmConfig.setDeviceName(deviceColDefine.getDeviceName());
        alarmConfig.setCol(deviceColDefine.getCol());
        alarmConfig.setColName(deviceColDefine.getColName());
        alarmConfig.setSensType(deviceColDefine.getSensType());
        Date now = new Date();
        alarmConfig.setCreateTime(now);
        alarmConfig.setUpdateTime(now);
        alarmConfig.setDeleted(CommonConstant.DELETED);
        alarmConfigMapper.insert(alarmConfig);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addSignalBatch(AlarmSignalBatchAddReq addReq) {
        //在alarmconfig表里查询判断是否有重复值,有就报错
        List<Integer> colIdList = addReq.getColIdList();
        //集团级别 ,stationId置空
        AlarmConfigPageReqVo alarmQuery = new AlarmConfigPageReqVo();
        alarmQuery.setDeviceType(addReq.getDeviceType());
        alarmQuery.setScope(addReq.getScope());
        alarmQuery.setColIds(colIdList);
        //电站级别才匹配电站参数,集团级别不用匹配电站参数
        if (FlowConstant.AlarmConfig.SCOPE_STATION.equals(addReq.getScope())) {
            alarmQuery.setStationId(addReq.getStationId());
        }
        List<AlarmConfig> alarmConfigs = selectList(alarmQuery);
        if (!alarmConfigs.isEmpty()) {
            //抛出已存在相关数据异常,并且把具体字段提示出来
            //把字段名称列出来,提示给客户
            List<String> colRepeatNames = new ArrayList<>();
            for (AlarmConfig alarmConfig : alarmConfigs) {
                colRepeatNames.add(alarmConfig.getColName());
            }
            String join = StringUtils.join(colRepeatNames.toArray(), ",");
            throw new BusinessException(BaseResponseCode.EXISTS_DUPLICATE_ALARM.getCode(), BaseResponseCode.EXISTS_DUPLICATE_ALARM.getMsg()+ join);
        }
        //这个时候可以新增数据了
        //先查这些数据对应的字段定义集合
        List<DeviceColDefine> deviceColDefines = deviceColDefineService.selectByIdList(colIdList, addReq.getDeviceType());
        //将类型转换为AlarmConfig
        List<AlarmConfig> configList = new ArrayList<>();
        for (DeviceColDefine deviceColDefine : deviceColDefines) {
            AlarmConfig alarmConfig = new AlarmConfig();
            alarmConfig.setGroupId(addReq.getGroupId());
            //只有电站的才需要增加电站属性
            if (FlowConstant.AlarmConfig.SCOPE_STATION.equals(addReq.getScope())) {
                alarmConfig.setStationId(addReq.getStationId());
            }
            alarmConfig.setScope(addReq.getScope());
            alarmConfig.setColDefineId(deviceColDefine.getId());
            alarmConfig.setDeviceTypeId(deviceColDefine.getDeviceTypeId());
            alarmConfig.setDeviceName(deviceColDefine.getDeviceName());
            alarmConfig.setDeviceType(deviceColDefine.getDeviceType());
            alarmConfig.setCol(deviceColDefine.getCol());
            alarmConfig.setColName(deviceColDefine.getColName());
            alarmConfig.setSensType(deviceColDefine.getSensType());
            alarmConfig.setInfo0(deviceColDefine.getInfo0());
            alarmConfig.setInfo1(deviceColDefine.getInfo1());
            alarmConfig.setAlarmLevel(addReq.getAlarmLevel());
            Date now = new Date();
            alarmConfig.setCreateTime(now);
            alarmConfig.setUpdateTime(now);
            alarmConfig.setDeleted(1);
            configList.add(alarmConfig);
        }
        //有数据就入库
        if (!configList.isEmpty()) {
            alarmConfigMapper.insertBatch(configList);
            String key = RedisKeyConstant.ALARM.GROUP_ALARM_CONFIG + addReq.getGroupId() + ":"+ addReq.getScope() + ":" + addReq.getDeviceType();
            if (FlowConstant.AlarmConfig.SCOPE_STATION.equals(addReq.getScope())) {
                key = RedisKeyConstant.ALARM.COMPANY_ALARM_CONFIG + addReq.getGroupId() + ":"+ addReq.getStationId() + ":" + addReq.getDeviceType();
            }
            addAlarmRedis(configList,key);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addMeasureBatch(AlarmMeasureBatchAddReq addReq) {
        List<MeasureVo> measureList = addReq.getMeasureList();
        if (measureList == null || measureList.isEmpty()) {
            return;
        }
        //类型是固定的,新增和修改是一个接口
        //一种是之前alarmconfig表没有的,新增进去
        //一种是之前有的，进行修改,要么是改值,要么删除
        //需要根据alarmconfig表中是否有这个id进行判断,如果没有就新增,对于有的,判断是修改还是删除

        //这个list和alarm表中的数据是一致的,只是需要把这里面的数据加工为新增、修改、删除3个子集合
        //第一种，要新增的,先按这个scope查询alarm中已经配置的
        AlarmConfigPageReqVo alarmConfigPageReqVo = new AlarmConfigPageReqVo();
        alarmConfigPageReqVo.setGroupId(addReq.getGroupId());
        alarmConfigPageReqVo.setDeviceType(addReq.getDeviceType());
        alarmConfigPageReqVo.setScope(addReq.getScope());
        alarmConfigPageReqVo.setSensType(addReq.getSensType());
        if (FlowConstant.AlarmConfig.SCOPE_STATION.equals(addReq.getScope())) {
            alarmConfigPageReqVo.setStationId(addReq.getStationId());
        }
        //先查已经配置的数据列表
        List<AlarmConfig> configList = alarmConfigMapper.selectList(alarmConfigPageReqVo);
        log.info("configList:{}" ,configList);
        Map<Integer, AlarmConfig> configMap = configList.stream().collect(Collectors.toMap(AlarmConfig::getColDefineId, alarmConfig -> alarmConfig));
        List<AlarmConfig> addNewList = new ArrayList<>();
        //用于删除的
        List<Integer> deleteList = new ArrayList<>();
        //用于修改的
        List<AlarmConfig> updateList = new ArrayList<>();
        //用于新增id
        List<Integer> defineIds = new ArrayList<>();
        Date now = new Date();
        Map<Integer,Integer> map = new HashMap<>();
        List<AlarmConfig> updateAllList = new ArrayList<>();
        for (MeasureVo measureVo : measureList) {
            if (!configMap.containsKey(measureVo.getId())) {
                if (measureVo.getLowerLimit() == null && measureVo.getUpperLimit() == null) {
                    continue;
                }
                //这个过程把要插入的数据都
                defineIds.add(measureVo.getId());
                AlarmConfig alarmConfigAdd = new AlarmConfig();
                alarmConfigAdd.setGroupId(addReq.getGroupId());
                alarmConfigAdd.setScope(addReq.getScope());
                //站级别，把stationId加进来
                if (FlowConstant.AlarmConfig.SCOPE_STATION.equals(addReq.getScope())) {
                    alarmConfigAdd.setStationId(addReq.getStationId());
                }
                alarmConfigAdd.setColDefineId(measureVo.getId());
                alarmConfigAdd.setSensType(addReq.getSensType());
                alarmConfigAdd.setLowerLimit(measureVo.getLowerLimit());
                alarmConfigAdd.setUpperLimit(measureVo.getUpperLimit());
                alarmConfigAdd.setCreateTime(now);
                alarmConfigAdd.setUpdateTime(now);
                addNewList.add(alarmConfigAdd);
            } else {
                AlarmConfig alarmConfig = configMap.get(measureVo.getId());
                map.put(alarmConfig.getId(),alarmConfig.getId());
                //这个时候是有这个字段的配置,继续判断是删除还是修改,根据 measureVo 这个值中的两个上下限是否同时为null，同时为null就是要删除的，不同时为null就是要修改的
                if (measureVo.getLowerLimit() == null && measureVo.getUpperLimit() == null) {
                    deleteList.add(alarmConfig.getId());
                } else {
                    //用于修改的
                    AlarmConfig alarmConfigUpdate = new AlarmConfig();
                    alarmConfigUpdate.setId(alarmConfig.getId());
                    //只修改上下限
                    alarmConfigUpdate.setUpperLimit(measureVo.getUpperLimit());
                    alarmConfigUpdate.setLowerLimit(measureVo.getLowerLimit());
                    updateList.add(alarmConfigUpdate);
                    AlarmConfig alarmConfigTotalUpdate = new AlarmConfig();
                    BeanUtils.copyProperties(alarmConfig,alarmConfigTotalUpdate);
                    alarmConfigTotalUpdate.setUpperLimit(measureVo.getUpperLimit());
                    alarmConfigTotalUpdate.setLowerLimit(measureVo.getLowerLimit());
                    alarmConfigTotalUpdate.setUpdateTime(now);
                    updateAllList.add(alarmConfigTotalUpdate);
                }
            }
        }
        //此时新增的集合还缺少基础信息,需要根据ids从字段定义表中查询
        //对新增、删除、修改的进行统一操作
        List<AlarmConfig> addAllList = new ArrayList<>();
        if (!defineIds.isEmpty()) {
            List<DeviceColDefine> deviceColDefines = deviceColDefineMapper.selectByIdList(defineIds, addReq.getDeviceType());
            if (!deviceColDefines.isEmpty()) {
                Map<Integer, DeviceColDefine> defineMap = deviceColDefines.stream().collect(Collectors.toMap(DeviceColDefine::getId, deviceColDefine -> deviceColDefine));
                for (AlarmConfig alarmConfig : addNewList) {
                    Integer key = alarmConfig.getColDefineId();
                    if (defineMap.containsKey(key)) {
                        DeviceColDefine deviceColDefine = defineMap.get(key);
                        alarmConfig.setCol(deviceColDefine.getCol());
                        alarmConfig.setColName(deviceColDefine.getColName());
                        alarmConfig.setDeviceTypeId(deviceColDefine.getDeviceTypeId());
                        alarmConfig.setDeviceType(addReq.getDeviceType());
                        alarmConfig.setDeviceName(deviceColDefine.getDeviceName());
                    }
                }
                log.info("addNewList:{}", addNewList);
                alarmConfigMapper.insertBatch(addNewList);
                addAllList.addAll(addNewList);
            }
        }
        log.info("deleteList:{}", deleteList);
        if (!deleteList.isEmpty()) {
            alarmConfigMapper.deleteBatch(deleteList);
        }
        log.info("updateList:{}", updateList);
        if (!updateList.isEmpty()) {
            for (AlarmConfig alarmConfig : updateList) {
                alarmConfigMapper.updateByPrimaryKeySelective(alarmConfig);
            }
            addAllList.addAll(updateAllList);
        }
        String key = RedisKeyConstant.ALARM.GROUP_ALARM_CONFIG + addReq.getGroupId() + ":"+ addReq.getScope() + ":" + addReq.getDeviceType();
        if (FlowConstant.AlarmConfig.SCOPE_STATION.equals(addReq.getScope())) {
            key = RedisKeyConstant.ALARM.COMPANY_ALARM_CONFIG + addReq.getGroupId() + ":"+ addReq.getStationId() + ":" + addReq.getDeviceType();
        }
        if(!map.isEmpty()){
            deleteAlarmRedis(map, key);
        }
        if(!addAllList.isEmpty()){
            addAlarmRedis(addAllList, key);
        }
    }

    @Override
    public void deletedById(Integer id) {
        AlarmConfig alarmConfig = alarmConfigMapper.selectById(id);
        alarmConfig.setDeleted(CommonConstant.DELETED_FLAG);
        alarmConfig.setUpdateTime(new Date());
        alarmConfigMapper.updateByPrimaryKeySelective(alarmConfig);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteSignalBatch(AlarmBatchDeleteReq deleteReq) {
        //先查到alarm表中用于删除的数据,然后把id组装成ids进行删除
        AlarmConfigPageReqVo queryReq = new AlarmConfigPageReqVo();
        queryReq.setGroupId(deleteReq.getGroupId());
        queryReq.setDeviceType(deleteReq.getDeviceType());
        queryReq.setScope(deleteReq.getScope());
        queryReq.setColIds(deleteReq.getColIds());
        //如果是站级就把 stationId塞进去
        if(FlowConstant.AlarmConfig.SCOPE_STATION.equals(deleteReq.getScope())){
            queryReq.setStationId(deleteReq.getStationId());
        }
        List<AlarmConfig> configList = alarmConfigMapper.selectList(queryReq);
        if(configList.isEmpty()){
            log.info("要删除的告警配置数据查询后为空");
            return ;
        }
        List<Integer> idForDeleteList = new ArrayList<>();
        Map<Integer,Integer> map = new HashMap<>();
        for (AlarmConfig alarmConfig : configList) {
            idForDeleteList.add(alarmConfig.getId());
            map.put(alarmConfig.getId(),alarmConfig.getId());
        }
        if(!idForDeleteList.isEmpty()){
            String key = RedisKeyConstant.ALARM.GROUP_ALARM_CONFIG + deleteReq.getGroupId() + ":"+ deleteReq.getScope() + ":" + deleteReq.getDeviceType();
            if (FlowConstant.AlarmConfig.SCOPE_STATION.equals(deleteReq.getScope())) {
                key = RedisKeyConstant.ALARM.COMPANY_ALARM_CONFIG  + deleteReq.getGroupId() + ":"+ deleteReq.getStationId() + ":" + deleteReq.getDeviceType();
            }
            deleteAlarmRedis(map,key);
            alarmConfigMapper.deleteBatch(idForDeleteList);
        }
    }

    @Override
    public int update(AlarmConfigUpdateReqVo vo) {
        AlarmConfig alarmConfig = new AlarmConfig();
        BeanUtils.copyProperties(vo, alarmConfig);
        alarmConfig.setStationId(null);
        //电站级别才匹配电站参数,集团级别不用匹配电站参数
        if (FlowConstant.AlarmConfig.SCOPE_STATION.equals(vo.getScope())) {
            alarmConfig.setStationId(vo.getStationId());
        }
        alarmConfig.setUpdateTime(new Date());
        return alarmConfigMapper.updateByParams(alarmConfig);
    }

    @Override
    public int syAlarmConfig(SynchronousReqVo vo) {
        try{
            Integer stationId = vo.getStationId();
            //查询告警配置定义
            DeviceColDefineReqVo definePageReq = new DeviceColDefineReqVo();
            definePageReq.setDeviceTypeSuffix(CommonConstant.JOIN_STATION_ID+stationId);
            List<DeviceColDefine> resultList = deviceColDefineService.selectList(definePageReq);
            //同步告警配置
            AlarmConfigPageReqVo alarmConfig = new AlarmConfigPageReqVo();
            alarmConfig.setDeviceTypeSuffix(CommonConstant.JOIN_STATION_ID+vo.getModelStationId());
            List<AlarmConfig> alarmList = alarmConfigMapper.selectList(alarmConfig);
            if(!alarmList.isEmpty()){
                Map<String, Integer> modelMap = resultList.stream().collect(Collectors.toMap(s -> s.getDeviceType() + CommonConstant.JOIN + s.getCol(), t -> t.getId(), (k1, k2) -> k1));
                List<AlarmConfig> insetList = new ArrayList<>();
                AlarmConfig ac = null;
                for (AlarmConfig alarm:alarmList) {
                    ac = new AlarmConfig();
                    BeanUtils.copyProperties(alarm,ac);
                    String type = alarm.getDeviceType().replace(CommonConstant.JOIN + vo.getModelStationId(), CommonConstant.JOIN + stationId);
                    ac.setColDefineId(modelMap.get(type+ CommonConstant.JOIN + alarm.getCol()));
                    ac.setDeviceType(type);
                    if(alarm.getStationId()!=null){
                        ac.setStationId(stationId);
                    }
                    insetList.add(ac);
                }
                if(!insetList.isEmpty()){
                    int result = alarmConfigMapper.deleteByParam(stationId, CommonConstant.JOIN + stationId);
                    log.info("删除旧的告警配置,电站：{},结果：{}",stationId,result);
                    List<List<AlarmConfig>> partition = Lists.partition(insetList, CommonConstant.NINE_HUNDRED_AND_NINETY_NINE);
                    for (List<AlarmConfig> list:partition) {
                        alarmConfigMapper.insertBatch(insetList);
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            log.info("同步告警配置发生异常：{}",e.getMessage());
        }
        return 0;
    }

    /**
     * 删除缓存中的告警配置
     * @param map
     * @param key
     */
    private void deleteAlarmRedis(Map<Integer, Integer> map, String key) {
        List<AlarmConfig> alarmList = (List<AlarmConfig>)redisService.get(key);
        if(alarmList!=null){
            alarmList = alarmList.stream().filter(i-> map.get(i.getId())==null).collect(Collectors.toList());
            if(alarmList.size()==0){
                redisService.delete(key);
            }else{
                redisService.set(key,alarmList);
                redisService.expire(key,1, TimeUnit.HOURS);
            }
        }
    }

    /**
     * 增加缓存中的告警配置
     * @param configList
     * @param key
     */
    private void addAlarmRedis(List<AlarmConfig> configList, String key) {
        List<AlarmConfig> alarmList = (List<AlarmConfig>)redisService.get(key);
        if(alarmList!=null){
            alarmList.addAll(configList);
        }else{
            alarmList = configList;
        }
        redisService.set(key,alarmList);
        redisService.expire(key,1, TimeUnit.HOURS);
    }
}
