package com.yuchen.saas.park.service.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.thinkunion.park.service.common.cache.CacheNames;
import com.yuchen.saas.mqtt.enums.IotProductEnum;
import com.yuchen.saas.park.api.entity.device.PmgBatSmartEquipment;
import com.yuchen.saas.park.api.entity.park.PmgBatDeviceData;
import com.yuchen.saas.park.api.entity.park.PmgBatParkInfo;
import com.yuchen.saas.park.api.util.DeviceDataUtils;
import com.yuchen.saas.park.api.vo.device.CountData;
import com.yuchen.saas.park.api.vo.device.DeviceProBaseCount;
import com.yuchen.saas.park.api.vo.device.RankData;
import com.yuchen.saas.park.api.vo.device.TrendData;
import com.yuchen.saas.park.api.vo.park.devicepro.DeviceBasePro;
import com.yuchen.saas.park.api.vo.park.devicepro.OneAmmeterDevicePro;
import com.yuchen.saas.park.api.vo.park.devicepro.ThreeAmmeterDevicePro;
import com.yuchen.saas.park.api.vo.park.devicepro.WaterMeterDevicePro;
import com.yuchen.saas.park.service.config.AsyncParkConfig;
import com.yuchen.saas.park.service.service.EnergyConsumptionService;
import com.yuchen.saas.park.service.service.ParkDeviceDataService;
import com.yuchen.saas.park.service.service.PmgBatParkInfoService;
import com.yuchen.saas.park.service.service.PmgBatSmartEquipmentService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.map.HashedMap;
import org.nest.springwrap.core.redis.cache.NestRedis;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @ClassName EnergyConsumptionService
 * @Description 能耗统计相关接口实现
 * @Author 张逸飞
 * @Date 2022/9/15 15:38
 */
@Service
@Slf4j
public class EnergyConsumptionServiceImpl implements EnergyConsumptionService {

    @Resource
    private NestRedis nestRedis;
    @Resource
    private ParkDeviceDataService dateService;
    @Resource
    private PmgBatSmartEquipmentService smartEquipmentService;

    @Resource(name = AsyncParkConfig.EXECUTOR_BEAN_NAME)
    private ThreadPoolTaskExecutor taskExecutor;

    @Resource
    private DeviceDataUtils deviceDataUtils;

    /**
     * 园区数据接口
     */
    @Resource
    private PmgBatParkInfoService pmgBatParkInfoService;

    /**
     * 水统计锁
     */
    private static ReentrantLock waterLock = new ReentrantLock();
    /**
     * 电统计锁
     */
    private static ReentrantLock powerThreeLock = new ReentrantLock();

    private static ReentrantLock powerOneLock = new ReentrantLock();


    //耗水量key
    private static String waterProductKey = IotProductEnum.WATER_METER_DEVICE.getValue();
    //耗电量key
    private static String powerThreeProductKey = IotProductEnum.THREE_AMMETER_DEVICE.getValue();

    private static String powerOneProductKey = IotProductEnum.ONE_AMMETER_DEVICE.getValue();


    /**
     * 获得水/电统计的锁
     *
     * @param productKey
     * @return
     */
    private static boolean tryLock(String productKey) {
        if (waterProductKey.equals(productKey)) {
            return waterLock.tryLock();
        }
        if (powerThreeProductKey.equals(productKey)) {
            return powerThreeLock.tryLock();
        }
        if (powerOneProductKey.equals(productKey)) {
            return powerOneLock.tryLock();
        }
        return false;
    }

    /**
     * 释放水电统计的锁
     *
     * @param productKey
     */
    private static void unLock(String productKey) {
        if (waterProductKey.equals(productKey)) {
            waterLock.unlock();
        }
        if (powerThreeProductKey.equals(productKey)) {
            powerThreeLock.unlock();
        }
        if (powerOneProductKey.equals(productKey)) {
            powerOneLock.unlock();
        }
    }

    @Override
    public void resetDeviceCountCash() {
        log.info("开始重建缓存");
        //清除缓存数据
        String statisticKeys = new StringBuilder(CacheNames.DEVICE_STATISTIC_CASH_KEY).append("*").toString();
        Set<String> keySet = nestRedis.keys(statisticKeys);
        if (keySet != null && keySet.size() > 0) {
            String[] keys = keySet.toArray(new String[keySet.size()]);
            log.info("清除缓存");
            nestRedis.del(keys);
        }
        //循环园区,重建缓存
        List<PmgBatParkInfo> allPark = pmgBatParkInfoService.getAllPark();
        allPark.forEach(park -> {
            this.getDeviceCount(park.getId());
        });
    }

    @Override
    public Map<String, Object> getDeviceCount(Long parkId) {
        log.info("开始获得统计数据 parkId: {}", parkId);
        Map<String, Object> resMap = new HashMap<>();

        //判断园区id是否存在 ,不存在直接返回
        if (parkId == null || parkId <= 0L) {
            return resMap;
        }
        PmgBatParkInfo byId = pmgBatParkInfoService.getById(parkId);
        if (byId == null) {
            return resMap;
        }
        // 统计水表数据
        DeviceProBaseCount waterProductCount = this.waterTotalCount(parkId);
        // 统计电表数据
        DeviceProBaseCount powerProductCount = this.powerTotalCount(parkId);
        //耗电量统计
        resMap.put("water", waterProductCount);
        resMap.put("power", powerProductCount);
        log.info("获得统计数据成功 parkId: {}", parkId);
        return resMap;
    }

    private DeviceProBaseCount waterTotalCount(Long parkId) {
        DeviceProBaseCount waterProductCount = doTotalCount(waterProductKey, parkId.toString(), WaterMeterDevicePro.DATA_IDENTIFIER);
        List<RankData> threeRankData = waterProductCount.getRankData() != null ? waterProductCount.getRankData() : new ArrayList<>();
        waterProductCount.setRankData(threeRankData);
        return waterProductCount;
    }

    /**
     * 获得三项&单项电表统计并进行循环
     *
     * @param parkId
     * @return
     */
    private DeviceProBaseCount powerTotalCount(Long parkId) {
        DeviceProBaseCount allCount = new DeviceProBaseCount();
        CountData countData = new CountData();
        TrendData trendData = new TrendData();
        List<RankData> rankData = new ArrayList<>();
        allCount.setCountData(countData);
        allCount.setTrendData(trendData);
        //allCount.setRankData(rankData);

        DeviceProBaseCount powerThreeProductKeyCount = doTotalCount(powerThreeProductKey, parkId.toString(), ThreeAmmeterDevicePro.DATA_IDENTIFIER);
        DeviceProBaseCount powerOneProductKeyCount = doTotalCount(powerOneProductKey, parkId.toString(), OneAmmeterDevicePro.DATA_IDENTIFIER);
        // 合并两种电表值
        CountData threeCountData = powerThreeProductKeyCount.getCountData() != null ? powerThreeProductKeyCount.getCountData() : new CountData();
        TrendData threeTrendData = powerThreeProductKeyCount.getTrendData() != null ? powerThreeProductKeyCount.getTrendData() : new TrendData();
        List<RankData> threeRankData = powerThreeProductKeyCount.getRankData() != null ? powerThreeProductKeyCount.getRankData() : new ArrayList<RankData>();

        CountData oneCountData = powerOneProductKeyCount.getCountData() != null ? powerOneProductKeyCount.getCountData() : new CountData();
        TrendData oneTrendData = powerOneProductKeyCount.getTrendData() != null ? powerOneProductKeyCount.getTrendData() : new TrendData();
        List<RankData> oneRankData = powerOneProductKeyCount.getRankData() != null ? powerOneProductKeyCount.getRankData() : new ArrayList<RankData>();

        countData.setToday(threeCountData.getToday() + oneCountData.getToday());
        countData.setLastMonth(threeCountData.getLastMonth() + oneCountData.getLastMonth());
        countData.setCurrentMonth(threeCountData.getCurrentMonth() + oneCountData.getCurrentMonth());
        countData.setTotal(threeCountData.getTotal() + oneCountData.getTotal());

        trendData.setToday0(threeTrendData.getToday0() + oneTrendData.getToday0());
        trendData.setToday1(threeTrendData.getToday1() + oneTrendData.getToday1());
        trendData.setToday2(threeTrendData.getToday2() + oneTrendData.getToday2());
        trendData.setToday3(threeTrendData.getToday3() + oneTrendData.getToday3());
        trendData.setToday4(threeTrendData.getToday4() + oneTrendData.getToday4());
        trendData.setToday5(threeTrendData.getToday5() + oneTrendData.getToday5());
        trendData.setToday6(threeTrendData.getToday6() + oneTrendData.getToday6());

        rankData.addAll(threeRankData);
        rankData.addAll(oneRankData);
        // 合并之后重新排序
        List<RankData> collect = rankData.stream()
                .sorted(Comparator.comparingInt(agr -> -Double.valueOf(agr.getValue()).intValue()))
                .collect(Collectors.toList());
        allCount.setRankData(collect);
        return allCount;
    }

    /**
     * 统计园区设备种类
     *
     * @param productKeys 需要查询的设备分类
     * @param parkId      指定园区的id
     * @return 统计数据
     */
    @Override
    public Map<String, Object> deviceNumber(String[] productKeys, String parkId) {
        // productKeys如果不传递, 默认需要查询的设备: 默认查询水电设备
        if (productKeys == null || productKeys.length == 0) {
            productKeys = new String[]{
                    IotProductEnum.THREE_AMMETER_DEVICE.getValue(),//三项电表统计
                    IotProductEnum.ONE_AMMETER_DEVICE.getValue(),//单项电表统计
                    IotProductEnum.WATER_METER_DEVICE.getValue()
            };
        }
        Map<String, Object> resMap = new HashMap<>();
        // 查询指定园区的设备
        List<DeviceBasePro> allDevice = new ArrayList<>();
        for (String productKey : productKeys) {
            List<DeviceBasePro> devicePro = deviceDataUtils.getDeviceDataFromCash(productKey, parkId);
            allDevice.addAll(devicePro);
        }
        // 总设备数量
        resMap.put("total", allDevice.size());
        AtomicInteger online = new AtomicInteger(0);
        AtomicInteger offline = new AtomicInteger(0);
        allDevice.forEach(info -> {
            // 设备的在线离线
            if (info.getIsOnline() == 1) {
                online.getAndIncrement();
            } else {
                offline.getAndIncrement();
            }
        });
        resMap.put("online", online);
        resMap.put("offline", offline);
        // 查询当前指定设备的个数
        for (int i = 0; i < productKeys.length; i++) {
            String productKey = productKeys[i];
            long count = allDevice.stream().filter(info -> info.getProductKey().equals(productKey)).count();
            // 水表
            if (IotProductEnum.WATER_METER_DEVICE.getValue().equals(productKey)) {
                resMap.put("water", count);
            }
            //三项
            if (IotProductEnum.THREE_AMMETER_DEVICE.getValue().equals(productKey)) {
                resMap.put("three", count);
            }
            //单项
            if (IotProductEnum.ONE_AMMETER_DEVICE.getValue().equals(productKey)) {
                resMap.put("one", count);
            }
        }
        long power = (long) resMap.get("three") + (long) resMap.get("one");
        resMap.put("power", power);
        return resMap;
    }

    /**
     * 可视化-》能耗管理-》点击下边的水电数据排名,显示统计数据
     *
     * @param parkId     园区ID
     * @param productKey 设备key
     * @param deviceName 设备ID
     * @return
     */
    @Override
    public Map<String, Object> singleDevice(Long parkId, String productKey, String deviceName) {
        // park:device:definite: + productKey + deviceKey
        String definiteDeviceKey = new StringBuilder(CacheNames.DEVICE_DEFINITE_CASH_KEY).append(parkId).append(":").append(productKey).append(":").append(deviceName).toString();
        String latestCashKey = new StringBuilder(CacheNames.DEVICE_LATEST_CASH_KEY).append(parkId).append(":").append(productKey).toString();

        Map<String, Object> cashMap = nestRedis.hGetAll(definiteDeviceKey);
        // 如果存在数据则直接返回数据
        if (!MapUtil.isEmpty(cashMap)) {
            return cashMap;
        }
        // 不存在则进行缓存重建
        // 2.计算单个电表数据
        CountData countData = new CountData();
        Map<Object, Object> latestData = new HashMap<>();
        // 查询
        DeviceBasePro deviceInfo = deviceDataUtils.getDeviceInfoFromCash(parkId.toString(), productKey, deviceName);

        if (StrUtil.isBlank(productKey) || (!waterProductKey.equals(productKey) && !powerThreeProductKey.equals(productKey) && !powerOneProductKey.equals(productKey))) {
            throw new RuntimeException("产品key错误");
        }
        String identify = "";
        if (waterProductKey.equals(productKey)) {
            identify = WaterMeterDevicePro.DATA_IDENTIFIER;
        }
        if (powerThreeProductKey.equals(productKey)) {
            identify = ThreeAmmeterDevicePro.DATA_IDENTIFIER;
        }
        if (powerOneProductKey.equals(productKey)) {
            identify = OneAmmeterDevicePro.DATA_IDENTIFIER;
        }
        //进行数据统计
        getDataFromDb(deviceInfo, countData, identify, latestData);

        //转换
        Map<String, Object> stringObjectMap = BeanUtil.beanToMap(countData);
        // 3.将统计数据放入缓存中
        nestRedis.getHashOps().putAll(definiteDeviceKey, stringObjectMap);
        // 4.更新最新走表数据
        nestRedis.hSet(latestCashKey, deviceName, latestData.get(deviceName));
        return stringObjectMap;
    }

    /**
     * 根据 parkId productKey identify 获得总的能耗,先从缓存中获得,未获得在进行计算
     *
     * @param productKey
     * @param parkId
     * @param identify
     * @return
     */
    private DeviceProBaseCount doTotalCount(String productKey, String parkId, String identify) {
        DeviceProBaseCount deviceProCount = new DeviceProBaseCount();
        // 先从缓存中取数据
        String countCashKey = new StringBuilder(CacheNames.DEVICE_COUNT_CASH_KEY).append(parkId).append(":").append(productKey).toString();
        String trendCashKey = new StringBuilder(CacheNames.DEVICE_TREND_CASH_KEY).append(parkId).append(":").append(productKey).toString();
        String rankCashKey = new StringBuilder(CacheNames.DEVICE_RANK_CASH_KEY).append(parkId).append(":").append(productKey).toString();
        String latestCashKey = new StringBuilder(CacheNames.DEVICE_LATEST_CASH_KEY).append(parkId).append(":").append(productKey).toString();
        Map countRedisMap = nestRedis.hGetAll(countCashKey);//能耗总量统计
        Map trendRedisMap = nestRedis.hGetAll(trendCashKey);//能耗趋势统计
        List<RankData> rankRedisList = nestRedis.get(rankCashKey);//能耗排名统计
        Map latestRedisMap = nestRedis.hGetAll(latestCashKey);//能耗最新数据备份
        if (MapUtil.isNotEmpty(countRedisMap)
                && MapUtil.isNotEmpty(trendRedisMap)
                && ArrayUtil.isNotEmpty(rankRedisList)
                && MapUtil.isNotEmpty(latestRedisMap)) {
            log.info("缓存中存在该园区统计数据 parkId: {}", parkId);
            // 缓存中存在以上四种需要的统计数据
            CountData redisCountData = BeanUtil.toBean(countRedisMap, CountData.class);
            TrendData redisTrendData = BeanUtil.toBean(trendRedisMap, TrendData.class);
            // 取出缓存中数据 ,放入返回对象中
            deviceProCount.setCountData(redisCountData);
            deviceProCount.setTrendData(redisTrendData);
            deviceProCount.setRankData(rankRedisList);
        } else {
            try {
                //尝试获得可重入锁
                boolean tryLock = tryLock(productKey);
                if (!tryLock) {
                    //如果没有拿到,说明别的线程在重建缓存
                    return deviceProCount;
                }
                log.info("能耗统计数据进行计算 start, parkId: {}", parkId);
                // 缓存中不存在数据则进行计算
                deviceProCount = this.doCalculate(productKey, parkId, identify);
                log.info("能耗统计数据进行计算 end, parkId: {}", parkId);
                if (deviceProCount != null) {
                    log.info("将能耗统计数据放入缓存中 start, parkId: {}", parkId);
                    // 将计算得到的统计数据放入缓存中
                    if (deviceProCount.getCountData() != null) {
                        Map<Object, Object> countMap = new HashedMap<>();
                        countMap.putAll(BeanUtil.beanToMap(deviceProCount.getCountData()));
                        //将统计数据数据放入缓存中
                        nestRedis.hMset(countCashKey, countMap);
                    }
                    if (deviceProCount.getTrendData() != null) {
                        Map<Object, Object> trendMap = new HashedMap<>();
                        trendMap.putAll(BeanUtil.beanToMap(deviceProCount.getTrendData()));
                        //将趋势数据放入缓存中
                        nestRedis.hMset(trendCashKey, trendMap);
                    }
                    if (deviceProCount.getRankData() != null) {
                        //将排名数据放入缓存中
                        List<RankData> rankData = deviceProCount.getRankData();
                        nestRedis.set(rankCashKey, rankData);
                    }
                    if (deviceProCount.getLatestData() != null) {
                        //将设备最新走表数据放入缓存中
                        nestRedis.hMset(latestCashKey, deviceProCount.getLatestData());
                    }
                    log.info("将能耗统计数据放入缓存中 end, parkId: {}", parkId);
                }
            } catch (Exception e) {
                log.error("重建缓存异常: {}", e.getMessage());
                throw new RuntimeException(e.getMessage());
            } finally {
                unLock(productKey);
            }
        }
        return deviceProCount;
    }

    /**
     * 根据 parkId productKey identify 计算总的能耗
     *
     * @param productKey
     * @param parkId
     * @param identify
     * @return
     */
    private DeviceProBaseCount doCalculate(String productKey, String parkId, String identify) {
        log.info("进行能耗统计计算 parkId: {} , productKey : {}", parkId, productKey);
        //缓存中不存在统计数据,则查询并计算
        DeviceProBaseCount deviceProCount = new DeviceProBaseCount();
        List<DeviceBasePro> deviceData = deviceDataUtils.getDeviceDataFromCash(productKey, parkId);
        if (deviceData == null || deviceData.size() == 0) {
            return deviceProCount;
        }
        // 这里家一层判断 判断当前园区设备是否全部未上传数据
        boolean uploadData = ifDeviceUploadData(deviceData, identify);
        if (!uploadData) {
            //如果未上传水电走表数据,直接返回,不进行下边的统计
            return deviceProCount;
        }
        CompletableFuture<CountData> countDataFuture = CompletableFuture.supplyAsync(() -> {
            try {
                return doCount(deviceData, identify, deviceProCount);
            } catch (Exception e) {
                throw new RuntimeException("能耗统计:园区能耗统计构建失败");
            }
        }, taskExecutor);
        CompletableFuture<TrendData> trendDataFuture = CompletableFuture.supplyAsync(() -> {
            try {
                return doTrend(deviceData, identify);
            } catch (Exception e) {
                throw new RuntimeException("能耗统计:园区耗水/电量趋势构建失败");
            }
        }, taskExecutor);
        CompletableFuture<List<RankData>> rankListFuture = CompletableFuture.supplyAsync(() -> {
            try {
                return doRank(deviceData, parkId, identify);
            } catch (Exception e) {
                throw new RuntimeException("能耗统计:累计耗水/电量排名构建失败");
            }
        }, taskExecutor);
        try {
            CountData countData = countDataFuture.get();
            TrendData trendData = trendDataFuture.get();
            List<RankData> rankList = rankListFuture.get();
            deviceProCount.setCountData(countData);
            deviceProCount.setTrendData(trendData);
            deviceProCount.setRankData(rankList);
        } catch (InterruptedException e) {
            throw new RuntimeException("线程被打断");
        } catch (ExecutionException e) {
            throw new RuntimeException(e.getMessage());
        } catch (Exception e) {
            throw new RuntimeException("系统未知异常");
        }
        return deviceProCount;
    }

    /**
     * 判断设备是否上传指定属性
     *
     * @param deviceData 设备列表
     * @param identify   指定查询属性
     * @return false 未上传数据 ; true 上传了数据
     */
    private boolean ifDeviceUploadData(List<DeviceBasePro> deviceData, String identify) {
        LambdaQueryWrapper<PmgBatDeviceData> query = new LambdaQueryWrapper<>();
        List<String> deviceName = deviceData.stream().map(DeviceBasePro::getDeviceName).collect(Collectors.toList());
        if (deviceName == null || deviceName.size() == 0) {
            return false;
        }
        query
                .eq(PmgBatDeviceData::getIdentify, identify)
                .in(PmgBatDeviceData::getDeviceName, deviceName);
        int count = dateService.count(query);
        return count == 0 ? false : true;
    }

    /**
     * 根据 parkId productKey identify  按 : 当日 当月 上月 总计 进行统计
     *
     * @param deviceData
     * @param identify
     * @return
     */
    private CountData doCount(List<DeviceBasePro> deviceData, String identify, DeviceProBaseCount deviceProCount) {
        //缓存中不存在统计数据,则查询并计算
        // 1.拿到最新的电/水表数据
        log.info("进行园区能耗统计计算");
        CountData countData = new CountData();
        Map<Object, Object> latestData = new HashMap<>();
        for (int i = 0; i < deviceData.size(); i++) {
            // 2.计算单个电表数据
            getDataFromDb(deviceData.get(i), countData, identify, latestData);
        }
        deviceProCount.setLatestData(latestData);
        return countData;
    }

    /**
     * 园区能耗趋势
     *
     * @param deviceData
     * @param identify
     * @return
     */
    private TrendData doTrend(List<DeviceBasePro> deviceData, String identify) {
        // 1.拿到最新的电/水表数据
        log.info("进行园区耗水/电量趋势计算");
        TrendData trendData = new TrendData();
        // 计算 y轴数据
        for (int i = 0; i < deviceData.size(); i++) {
            DeviceBasePro deviceBasePro = deviceData.get(i);
            // 当前电表累计用电量数据数据
            Double currentData = queryCurrentData(deviceBasePro, identify);
            Date deviceTime = new Date();//当前时间
            DateTime today = DateUtil.beginOfDay(deviceTime);// 当日 开始时间
            DateTime today_1 = DateUtil.beginOfDay(DateUtil.offsetDay(deviceTime, -1));// 前一天时间
            DateTime today_2 = DateUtil.beginOfDay(DateUtil.offsetDay(deviceTime, -2));// 前两天时间
            DateTime today_3 = DateUtil.beginOfDay(DateUtil.offsetDay(deviceTime, -3));
            DateTime today_4 = DateUtil.beginOfDay(DateUtil.offsetDay(deviceTime, -4));
            DateTime today_5 = DateUtil.beginOfDay(DateUtil.offsetDay(deviceTime, -5));
            DateTime today_6 = DateUtil.beginOfDay(DateUtil.offsetDay(deviceTime, -6));

            double todayData = queryDeviceValueByDate(today, deviceBasePro, identify);
            double todayData_1 = queryDeviceValueByDate(today_1, deviceBasePro, identify);
            double todayData_2 = queryDeviceValueByDate(today_2, deviceBasePro, identify);
            double todayData_3 = queryDeviceValueByDate(today_3, deviceBasePro, identify);
            double todayData_4 = queryDeviceValueByDate(today_4, deviceBasePro, identify);
            double todayData_5 = queryDeviceValueByDate(today_5, deviceBasePro, identify);
            double todayData_6 = queryDeviceValueByDate(today_6, deviceBasePro, identify);

            trendData.setToday0(trendData.getToday0() + currentData - todayData);
            trendData.setToday1(trendData.getToday1() + todayData - todayData_1);
            trendData.setToday2(trendData.getToday2() + todayData_1 - todayData_2);
            trendData.setToday3(trendData.getToday3() + todayData_2 - todayData_3);
            trendData.setToday4(trendData.getToday4() + todayData_3 - todayData_4);
            trendData.setToday5(trendData.getToday5() + todayData_4 - todayData_5);
            trendData.setToday6(trendData.getToday6() + todayData_5 - todayData_6);
        }

        return trendData;
    }

    /**
     * 园区能耗排名
     *
     * @param deviceData
     * @param identify
     * @return
     */
    private List<RankData> doRank(List<DeviceBasePro> deviceData, String parkId, String identify) {
        log.info("进行园区耗水/电量排名计算");
        List<RankData> rankDataList = new ArrayList<>();
        // 1.拿到最新的电/水表数据
        DecimalFormat df = new DecimalFormat("0.00");
        deviceData.forEach(pro -> {
            RankData rankData = new RankData();
            double todayData = queryCurrentData(pro, identify);
            rankData.setLabel(pro.getName());//设置名称
            rankData.setValue(df.format(todayData));//设置累计数据(排名数据)
            Map<String, Object> map = singleDevice(Long.valueOf(parkId), pro.getProductKey(), pro.getDeviceName());
            String combine = new StringBuilder(rankData.getValue()).append("|")
                    .append(map.get("today")).append("|")
                    .append(map.get("currentMonth")).append("|")
                    .append(map.get("lastMonth")).append("|")
                    .append(map.get("total")).toString();
            rankData.setCombine(combine);
            rankDataList.add(rankData);
        });
        //排序
        List<RankData> collect = rankDataList.stream()
                .sorted(Comparator.comparingInt(agr -> -Double.valueOf(agr.getValue()).intValue()))
                .collect(Collectors.toList());

        return collect;
    }

    /**
     * 统计单个设备 当日 当月 上月 总计 进行能耗
     *
     * @param deviceBasePro
     * @param countData
     * @param identify
     */
    private void getDataFromDb(DeviceBasePro deviceBasePro, CountData countData, String identify, Map<Object, Object> latestData) {
        Date deviceTime = new Date();//当前时间
        DateTime today = DateUtil.beginOfDay(deviceTime);// 当日 开始时间
        DateTime currentMonth = DateUtil.beginOfMonth(deviceTime);// 当前月份开始日期
        DateTime lastMonthBeginDay = DateUtil.beginOfMonth(DateUtil.offsetMonth(deviceTime, -1));// 上一个月开始时间
        DateTime lastMonthEndDay = DateUtil.endOfMonth(DateUtil.offsetMonth(deviceTime, -1)); //上一个月结束时间
        // 当前电表累计用电量数据数据
        double currentData = queryCurrentData(deviceBasePro, identify);
        // 查询当天开始时候的电表数据
        double dayBeginData = queryDeviceValueByDate(today, deviceBasePro, identify);
        // 查询当月用开始用电量
        double monthBeginData = queryDeviceValueByDate(currentMonth, deviceBasePro, identify);
        // 查询上月用开始用电量
        double lastMonthBeginData = queryDeviceValueByDate(lastMonthBeginDay, deviceBasePro, identify);
        // 查询上月用结束用电量
        double lastMonthEndData = queryDeviceValueByDate(lastMonthEndDay, deviceBasePro, identify);
        // 查询最开始有数据记录时候的电表值
        //当日用量
        countData.setToday(countData.getToday() + currentData - dayBeginData);
        //当月用电量
        countData.setCurrentMonth(countData.getCurrentMonth() + currentData - monthBeginData);
        //上月用电量
        countData.setLastMonth(countData.getLastMonth() + lastMonthEndData - lastMonthBeginData);
        //总用电量
        countData.setTotal(countData.getTotal() + currentData);
        //设置最新的能耗统计数据
        //countData.setLatestData(countData.getTotal());
        latestData.put(deviceBasePro.getDeviceName(), currentData);
    }


    /**
     * 根据 parkId  productKey 获得所有的设备信息
     *
     * @param productKey
     * @param parkId
     * @return
     */
    private List<PmgBatSmartEquipment> queryDeviceDataFromDb(String productKey, String parkId) {
        // 查询指定园区的设备
        LambdaQueryWrapper<PmgBatSmartEquipment> equipmentQueryWrapper = new LambdaQueryWrapper();
        equipmentQueryWrapper
                .eq(PmgBatSmartEquipment::getParkId, parkId)
                .eq(PmgBatSmartEquipment::getProductKey, productKey);
        //查询指定园区指定设备
        List<PmgBatSmartEquipment> allDevice = smartEquipmentService.list(equipmentQueryWrapper);
        return allDevice;
    }

    /**
     * 获得指定日期的设备属性数据
     *
     * @param date
     * @param deviceBasePro
     * @param identify
     * @return
     */
    private double queryDeviceValueByDate(Date date, DeviceBasePro deviceBasePro, String identify) {
        LambdaQueryWrapper<PmgBatDeviceData> query = new LambdaQueryWrapper<>();
        query
                .eq(PmgBatDeviceData::getProductKey, deviceBasePro.getProductKey())
                .eq(PmgBatDeviceData::getDeviceName, deviceBasePro.getDeviceName())
                .eq(PmgBatDeviceData::getIdentify, identify)
                .lt(PmgBatDeviceData::getDeviceTime, date)
                .orderByDesc(PmgBatDeviceData::getDeviceTime)
                .last("LIMIT 1");
        PmgBatDeviceData deviceData = dateService.getOne(query);
        if (deviceData == null) {
            return 0D;
        }
        return NumberUtil.parseDouble(deviceData.getValue());
    }

    /**
     * 查询最新的设备数据
     *
     * @param deviceBasePro
     * @param identify
     * @return
     */
    private double queryCurrentData(DeviceBasePro deviceBasePro, String identify) {
        LambdaQueryWrapper<PmgBatDeviceData> query = new LambdaQueryWrapper<>();
        query
                .eq(PmgBatDeviceData::getProductKey, deviceBasePro.getProductKey())
                .eq(PmgBatDeviceData::getDeviceName, deviceBasePro.getDeviceName())
                .eq(PmgBatDeviceData::getIdentify, identify)
                .orderByDesc(PmgBatDeviceData::getDeviceTime)
                .last("LIMIT 1");
        PmgBatDeviceData deviceData = dateService.getOne(query);
        if (deviceData == null) {
            return 0D;
        }
        return NumberUtil.parseDouble(deviceData.getValue());
    }
}
