package com.mortals.xhx.module.device.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import com.mortals.framework.common.Rest;
import com.mortals.framework.model.Context;
import com.mortals.framework.model.OrderCol;
import com.mortals.framework.service.impl.AbstractCRUDServiceImpl;
import com.mortals.xhx.common.code.AlarmLevelEnum;
import com.mortals.xhx.common.code.AlarmTypeEnum;
import com.mortals.xhx.common.code.DeviceSrcEnum;
import com.mortals.xhx.common.code.YesNoEnum;
import com.mortals.xhx.common.pdu.RespData;
import com.mortals.xhx.common.pdu.site.SitePdu;
import com.mortals.xhx.feign.site.ISiteFeign;
import com.mortals.xhx.module.device.dao.DeviceAlarmInfoDao;
import com.mortals.xhx.module.device.model.DeviceAlarmInfoEntity;
import com.mortals.xhx.module.device.model.DeviceAlarmInfoQuery;
import com.mortals.xhx.module.device.model.DeviceEntity;
import com.mortals.xhx.module.device.model.DeviceQuery;
import com.mortals.xhx.module.device.service.DeviceAlarmInfoService;
import com.mortals.xhx.module.device.service.DeviceService;
import com.mortals.xhx.module.firm.model.FirmEntity;
import com.mortals.xhx.module.firm.service.FirmService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

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

import static java.math.BigDecimal.ROUND_HALF_DOWN;
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;

/**
 * DeviceAlarmInfoService
 * 设备告警日志 service实现
 *
 * @author zxfei
 * @date 2022-06-27
 */
@Service("deviceAlarmInfoService")
public class DeviceAlarmInfoServiceImpl extends AbstractCRUDServiceImpl<DeviceAlarmInfoDao, DeviceAlarmInfoEntity, Long> implements DeviceAlarmInfoService {

    @Autowired
    private DeviceService deviceService;
    @Autowired
    private ISiteFeign siteFeign;
    @Autowired
    private FirmService firmService;

    @Override
    public Rest<DeviceAlarmInfoEntity> alarmStats(DeviceAlarmInfoEntity deviceAlarmInfo, Context context) {
        try {
            Rest<List<SitePdu>> rest = siteFeign.getFlatSitesBySiteId(new SitePdu().id(deviceAlarmInfo.getSiteId()));
            //  Rest<RespData<List<SitePdu>>> rest = siteFeign.list(new SitePdu());
            if (rest.getCode() == YesNoEnum.NO.getValue()) {
                return Rest.fail("获取站点列表异常！");
            }
            Map<Long, SitePdu> siteMap = rest.getData().stream().collect(Collectors.toMap(x -> x.getId(), y -> y, (o, n) -> n));

            Map<Long, FirmEntity> FirmMap = firmService.findToMap(new FirmEntity(), context);

            Long siteId = deviceAlarmInfo.getSiteId();

            DeviceAlarmInfoQuery deviceAlarmInfoQuery = new DeviceAlarmInfoQuery();
            deviceAlarmInfoQuery.setAlarmTimeStart(DateUtil.today());
            deviceAlarmInfoQuery.setAlarmTimeEnd(DateUtil.today());
            deviceAlarmInfoQuery.setOrderColList(Arrays.asList(new OrderCol("alarmTime",OrderCol.DESCENDING)));

            List<DeviceAlarmInfoEntity> deviceAlarmInfoList = this.find(deviceAlarmInfoQuery);
            //统计总设备数量
            List<DeviceEntity> deviceList = deviceService.find(new DeviceQuery().siteId(siteId));
            //设备总量
            Integer deviceTotalCount = deviceList.size();
            deviceAlarmInfo.setDeviceTotalCount(deviceTotalCount);
            //监控站点数量
            int siteSize = rest.getData().size();
            deviceAlarmInfo.setSiteSize(siteSize);

            //分站点统计设备数量
            Map<String, Long> siteDeviceCollect = deviceList.parallelStream().collect(groupingBy(x -> x.getSiteName(), counting()));
            deviceAlarmInfo.setSiteDeviceCollect(siteDeviceCollect);
            //分站点统计设备今日告警数量
            Map<String, Long> siteDeviceAlarmCollect = deviceAlarmInfoList
                    .parallelStream().distinct().collect(groupingBy(x -> x.getSiteId(), counting()))
                    .entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                    .collect(Collectors.toMap(x -> siteMap.get(x.getKey()) == null ? "未知站点" : siteMap.get(x.getKey()).getSiteName(), y -> y.getValue(), (o, n) -> n));
            deviceAlarmInfo.setSiteDeviceAlarmCollect(siteDeviceAlarmCollect);
            //设备类型分布
            Map<String, String> deviceConnTypeCollect = deviceList.
                    parallelStream().collect(groupingBy(x -> DeviceSrcEnum.getByValue(x.getDeviceSrc()).getDesc(), Collectors.collectingAndThen(counting(), y ->
                            NumberUtil.div(y, deviceTotalCount).multiply(new BigDecimal(100)).setScale(2, ROUND_HALF_DOWN).toString()
                    )));
            deviceAlarmInfo.setDeviceConnTypeCollect(deviceConnTypeCollect);

            //设备厂商分布
            Map<String, String> deviceFirmCollect = deviceList.parallelStream().collect(groupingBy(x -> FirmMap.get(x.getDeviceFirmId()) == null ? "未知厂商" : FirmMap.get(x.getDeviceFirmId()).getFirmName(), Collectors.collectingAndThen(counting(), y ->
                    NumberUtil.div(y, deviceTotalCount).multiply(new BigDecimal(100)).setScale(2, ROUND_HALF_DOWN).toString()
            )));
            deviceAlarmInfo.setDeviceFirmCollect(deviceFirmCollect);
            //今日告警次数
            int todayAlarmInfoCount = deviceAlarmInfoList.size();
            deviceAlarmInfo.setTodayAlarmInfoCount(todayAlarmInfoCount);

            //今日告警设备数量
            long todayAlarmDeviceCount = deviceAlarmInfoList.parallelStream().distinct().count();
            deviceAlarmInfo.setTodayAlarmDeviceCount(todayAlarmDeviceCount);

            //告警类型分布
            Map<String, String> alarmTypeCollect = deviceAlarmInfoList.parallelStream().collect(groupingBy(x -> AlarmTypeEnum.getByValue(x.getAlarmType()).getDesc(), Collectors.collectingAndThen(counting(), y ->
                    NumberUtil.div(String.valueOf(y), String.valueOf(todayAlarmInfoCount)).multiply(new BigDecimal(100)).setScale(2, ROUND_HALF_DOWN).toString()
            )));
            deviceAlarmInfo.setAlarmTypeCollect(alarmTypeCollect);
            //告警等级分布
            Map<String, String> alarmLevelCollect = deviceAlarmInfoList.parallelStream().collect(groupingBy(x -> AlarmLevelEnum.getByValue(x.getAlarmLevel()).getDesc(), Collectors.collectingAndThen(counting(), y ->
                    NumberUtil.div(String.valueOf(y), String.valueOf(todayAlarmInfoCount)).multiply(new BigDecimal(100)).setScale(2, ROUND_HALF_DOWN).toString()
            )));
            deviceAlarmInfo.setAlarmLevelCollect(alarmLevelCollect);
            Map<Long, Long> deviceAlarmCountMap = deviceAlarmInfoList.parallelStream().collect(groupingBy(x -> x.getAlarmDevice(), counting()));

            //最近的告警信息 选择前20条
            List<HashMap<String, Object>> deviceAlarmMapInfoList = deviceAlarmInfoList.stream().limit(20).map(item -> {
                HashMap<String, Object> map = new HashMap<>();
                map.put("id", item.getId());
                map.put("deviceName", item.getDeviceName());
                map.put("count", deviceAlarmCountMap.getOrDefault(item.getAlarmDevice(), 1L));
                map.put("alarmTime", DateUtil.format(item.getAlarmTime(), DatePattern.NORM_TIME_PATTERN));
                map.put("alarmType", AlarmTypeEnum.getByValue(item.getAlarmType()).getDesc());
                return map;
            }).collect(Collectors.toList());
            deviceAlarmInfo.setDeviceAlarmMapInfoList(deviceAlarmMapInfoList);

            return Rest.ok(deviceAlarmInfo);
        } catch (Exception e) {
            log.error("设备告警信息统计异常", e);
            return Rest.fail(e.getMessage());
        }
    }

    @Override
    public Rest<List<DeviceAlarmInfoEntity>> alarmStatList(DeviceAlarmInfoEntity deviceAlarmInfo, Context context) {
        ArrayList<DeviceAlarmInfoEntity> infoList = new ArrayList<>();
        try {
            Rest<RespData<List<SitePdu>>> rest = siteFeign.list(new SitePdu());
            Map<Long, SitePdu> siteMap = rest.getData().getData().stream().collect(Collectors.toMap(x -> x.getId(), y -> y, (o, n) -> n));
            if (ObjectUtils.isEmpty(siteMap)) {
                return Rest.fail("获取站点列表异常！");
            }
            Long siteId = deviceAlarmInfo.getSiteId();
            DeviceAlarmInfoQuery deviceAlarmInfoQuery = new DeviceAlarmInfoQuery();
            deviceAlarmInfoQuery.setSiteId(siteId);
            deviceAlarmInfoQuery.setAlarmTimeStart(DateUtil.today());
            deviceAlarmInfoQuery.setAlarmTimeEnd(DateUtil.today());
            deviceAlarmInfoQuery.setOrderColList(Arrays.asList(new OrderCol("alarmTime", OrderCol.DESCENDING)));
            List<DeviceAlarmInfoEntity> deviceAlarmInfoList = this.find(deviceAlarmInfoQuery);
            Map<Long, Map<Integer, List<DeviceAlarmInfoEntity>>> collect = deviceAlarmInfoList.parallelStream().collect(groupingBy(x -> x.getAlarmDevice(), groupingBy(y -> y.getAlarmType())));

            collect.entrySet().stream().forEach(entry -> {
                Long key = entry.getKey();
                Map<Integer, List<DeviceAlarmInfoEntity>> listMap = entry.getValue();
                listMap.entrySet().stream().forEach(entry1 -> {
                    List<DeviceAlarmInfoEntity> vals = entry1.getValue();
                    DeviceAlarmInfoEntity deviceAlarmInfoEntity = new DeviceAlarmInfoEntity();
                    deviceAlarmInfoEntity.initAttrValue();
                    DeviceEntity extCache = deviceService.getCache(key.toString());
                    deviceAlarmInfoEntity.setDevice(extCache);
                    deviceAlarmInfoEntity.setAlarmTime(vals.get(0).getAlarmTime());
                    deviceAlarmInfoEntity.setAlarmTypeStr(AlarmTypeEnum.getByValue(vals.get(0).getAlarmType()).getDesc());
                    deviceAlarmInfoEntity.setTodayAlarmInfoCount(vals.size());
                    infoList.add(deviceAlarmInfoEntity);
                });
            });
            return Rest.ok(infoList);
        } catch (Exception e) {
            log.error("设备告警信息统计异常", e);
            return Rest.fail(e.getMessage());
        }

    }
}