package com.yuchen.saas.park.service.pull.input;

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.StrUtil;
import com.thinkunion.park.service.common.cache.CacheNames;
import com.yuchen.saas.park.api.entity.device.PmgBatSmartEquipment;
import com.yuchen.saas.park.api.entity.park.PmgBatDeviceData;
import com.yuchen.saas.park.api.vo.device.CountData;
import com.yuchen.saas.park.api.vo.device.RankData;
import com.yuchen.saas.park.api.vo.fault.DeviceProperty;
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.factory.SimpleDeviceFactory;
import com.yuchen.saas.park.service.fault.service.EquipmentStatusService;
import com.yuchen.saas.park.service.pull.property.template.DeviceHandleBasePropertyValue;
import com.yuchen.saas.park.service.service.EnergyConsumptionService;
import com.yuchen.saas.park.service.service.ParkDeviceDataService;
import com.yuchen.saas.park.service.service.PmgBatSmartEquipmentService;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.map.HashedMap;
import org.nest.springwrap.core.redis.cache.NestRedis;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 废弃
 * @ClassName DevicePropertyInput
 * @Description 设备属性上报处理
 * @Author 吕海泉
 * @Date 2020/12/15 0:57
 */
@Service
public class DevicePropertyInput {

    private final Logger logger = LoggerFactory.getLogger(DevicePropertyInput.class);

    /**
     * 注入园区设备操作接口
     */
    @Resource
    private PmgBatSmartEquipmentService smartEquipmentService;

    /**
     * 注入设备属性数据操作接口
     */
    @Resource
    private ParkDeviceDataService parkDeviceDataService;

    @Lazy
    @Resource
    protected NestRedis nestRedis;

    @Resource
    private EnergyConsumptionService energyConsumptionService;


    @Resource
    private EquipmentStatusService equipmentStatusService;

    /**
     * 处理设备属性上报事件
     *
     * @param deviceProperty 设备属性数据
     */
    public void inputData(DeviceProperty deviceProperty) {
        // 获取设备的 productKey 和 deviceName
        String productKey = deviceProperty.getProductKey();
        String deviceName = deviceProperty.getDeviceName();
        // 获取设备详细信息
        PmgBatSmartEquipment deviceInfo = smartEquipmentService.getDetails(productKey, deviceName);
        if (null != deviceInfo) {
            // 保存设备属性数据
            saveDeviceData(deviceProperty, deviceInfo);
            // 缓存设备数据
            cashDeviceData(productKey, deviceProperty, deviceInfo);
            // 根据设备属性值判断是否需要进一步操作
            baseAttributeValue(deviceProperty, deviceInfo);
            // 设备故障记录
            equipmentStatusService.saveEquipmentStatus(deviceProperty, deviceInfo);
        }
    }

    /**
     * 根据设备的属性值,进行进一步数据操作
     *
     * @param deviceProperty iot传递进来的属性值
     * @param deviceInfo     设备详细信息
     */
    private void baseAttributeValue(DeviceProperty deviceProperty, PmgBatSmartEquipment deviceInfo) {
        DeviceHandleBasePropertyValue deviceHandle = SimpleDeviceFactory.createDeviceHandle(deviceProperty.getProductKey());
        if (deviceHandle != null) {
            deviceHandle.doHandle(deviceProperty, deviceInfo);
        }
    }

    /**
     * @return void
     * @Author 肖润飞
     * @Description 缓存统计数据
     * @CreateTime 2022/9/6 12:01
     * @Param [productKey:产品key值,deviceProperty:传递进来的数据 ,deviceInfo : 设备数据]
     **/
    private void cashDeviceData(String productKey, DeviceProperty deviceProperty, PmgBatSmartEquipment deviceInfo) {
        // cash_key 示例  park:device:productKey:deviceName
        String cashKey = new StringBuilder(CacheNames.DEVICE_PRO_PREFIX).append(deviceInfo.getParkId()).append(":").append(productKey).append(":").append(deviceInfo.getDeviceName()).toString();
        Boolean exists = nestRedis.exists(cashKey);
        Map<Object, Object> objectObjectMap = populationDevicePro(productKey, deviceProperty, deviceInfo, exists);
        if (MapUtils.isNotEmpty(objectObjectMap)) {
            // 将数据存入redis, 存在的话就直接覆盖
            nestRedis.hMset(cashKey, objectObjectMap);
            // 修改单个水电设备的数据统计
            updateSingleDeviceData(deviceProperty, deviceInfo);
            // 修改能耗统计
            updateRedisCount(deviceProperty, deviceInfo.getName(), deviceInfo.getParkId(), deviceProperty.getValue());
            // ws推送 能耗管理页面 | 智慧浇灌页面 |   物联网感知页面 更改数据
            // pushDeviceProUtil.sendWs(productKey, deviceProperty, deviceInfo.getParkId());
        }
    }

    /**
     * 修改单个设备的数据统计信息
     *
     * @param deviceProperty
     * @param deviceInfo
     */
    private void updateSingleDeviceData(DeviceProperty deviceProperty, PmgBatSmartEquipment deviceInfo) {
        if (!WaterMeterDevicePro.DATA_IDENTIFIER.equals(deviceProperty.getIdentify())
                && !ThreeAmmeterDevicePro.DATA_IDENTIFIER.equals(deviceProperty.getIdentify())
                && !OneAmmeterDevicePro.DATA_IDENTIFIER.equals(deviceProperty.getIdentify())) {
            return;
        }
        Long parkId = deviceInfo.getParkId();
        String productKey = deviceInfo.getProductKey();
        String deviceName = deviceInfo.getDeviceName();
        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(deviceProperty.getProductKey()).toString();
        Map<String, Object> countRedisMap = nestRedis.hGetAll(definiteDeviceKey);
        Map<String, Object> latestCashMap = nestRedis.hGetAll(latestCashKey);
        // 如果存在数据则直接返回数据
        if (MapUtil.isEmpty(countRedisMap) || MapUtil.isEmpty(latestCashMap)) {
            //缓存中不存在则重建缓存
            energyConsumptionService.singleDevice(parkId, productKey, deviceName);
            return;
        }
        // 修改缓存信息
        CountData redisCountData = BeanUtil.toBean(countRedisMap, CountData.class);
        double newValue = Double.valueOf(deviceProperty.getValue() + "");
        double addValue = newValue - (Double) latestCashMap.get(deviceProperty.getDeviceName());

        // 修改统计数据
        redisCountData.setToday(addValue + redisCountData.getToday());//当日数据
        redisCountData.setCurrentMonth(addValue + redisCountData.getCurrentMonth());//当月数据
        redisCountData.setTotal(addValue + redisCountData.getTotal());//累计数据
        // 更新单个设备统计数据
        Map<String, Object> stringObjectMap = BeanUtil.beanToMap(redisCountData);
        nestRedis.getHashOps().putAll(definiteDeviceKey, stringObjectMap);
    }

    private void updateRedisCount(DeviceProperty deviceProperty, String name, Long parkId, Object value) {
        if (!WaterMeterDevicePro.DATA_IDENTIFIER.equals(deviceProperty.getIdentify())
                && !ThreeAmmeterDevicePro.DATA_IDENTIFIER.equals(deviceProperty.getIdentify())
                && !OneAmmeterDevicePro.DATA_IDENTIFIER.equals(deviceProperty.getIdentify())) {
            return;
        }
        String productKey = deviceProperty.getProductKey();
        String deviceName = deviceProperty.getDeviceName();
        double newValue = Double.valueOf(value + "");
        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();
        // 判断统计数据存在与否
        Boolean countBoolean = nestRedis.exists(countCashKey);
        Boolean trendBoolean = nestRedis.exists(trendCashKey);
        Boolean rankBoolean = nestRedis.exists(rankCashKey);
        Boolean latestBoolean = nestRedis.exists(latestCashKey);
        if (!countBoolean || !trendBoolean || !rankBoolean || !latestBoolean) {
            //四个缓存key其中一个不存在,则重建redis缓存统计
            energyConsumptionService.getDeviceCount(parkId);
            return;
        }
        // 1.修改耗能统计
        Map countRedisMap = nestRedis.hGetAll(countCashKey);
        Map latestCashMap = nestRedis.hGetAll(latestCashKey);
        double today0 = nestRedis.hGet(trendCashKey, "today0");
        List<RankData> rankRedisList = nestRedis.get(rankCashKey);
        CountData redisCountData = BeanUtil.toBean(countRedisMap, CountData.class);
        //新增的能耗统计数据
        double addValue = newValue - (Double) latestCashMap.get(deviceName);
        // 修改统计数据
        redisCountData.setToday(addValue + redisCountData.getToday());//当日数据
        redisCountData.setCurrentMonth(addValue + redisCountData.getCurrentMonth());//当月数据
        redisCountData.setTotal(addValue + redisCountData.getTotal());//累计数据
        // 2.修改趋势数据
        today0 = today0 + addValue;
        // 3.修改排名数据
        // 先判断是否存在该设备排名
        long count = rankRedisList.stream().filter(rankData -> rankData.getLabel().equals(name)).count();
        if (count == 0) {
            //如果是新加设备
            RankData rankData = new RankData();
            rankData.setLabel(name);
            rankData.setValue(value.toString());
            // 修改设备的拼接信息
            Map<String, Object> map = energyConsumptionService.singleDevice(parkId, productKey, deviceName);
            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);
            rankRedisList.add(rankData);
        } else {
            rankRedisList.forEach(rank -> {
                if (rank.getLabel().equals(name)) {
                    Map<String, Object> map = energyConsumptionService.singleDevice(parkId, productKey, deviceName);
                    // 如果是已经存在的设备
                    rank.setValue(value.toString());
                    String combine = new StringBuilder(rank.getValue()).append("|")
                            .append(map.get("today")).append("|")
                            .append(map.get("currentMonth")).append("|")
                            .append(map.get("lastMonth")).append("|")
                            .append(map.get("total")).toString();
                    rank.setCombine(combine);
                }
            });
        }
        // 更新统计数据
        Map<String, Object> stringObjectMap = BeanUtil.beanToMap(redisCountData);
        if (MapUtils.isNotEmpty(stringObjectMap)) {
            Map<Object, Object> resMap = new HashMap<>();
            resMap.putAll(stringObjectMap);
            nestRedis.hMset(countCashKey, resMap);
        }
        //更新趋势数据
        nestRedis.hSet(trendCashKey, "today0", today0);
        //更新排名数据
        nestRedis.set(rankCashKey, rankRedisList);
        //更新最新走表数据
        nestRedis.hSet(latestCashKey, deviceName, newValue);
    }

    /**
     * @return Map
     * @Author 肖润飞
     * @Description 将iot传递来的数据封装到 DeviceBasePro中并转换成map
     * @CreateTime 2022/9/6 17:41
     * @Param [productKey:产品key值,deviceProperty:传递进来的数据 ,deviceInfo : 设备数据 , exists:缓存的key是否已经存在]
     **/
    private Map<Object, Object> populationDevicePro(String productKey, DeviceProperty deviceProperty, PmgBatSmartEquipment deviceInfo, Boolean exists) {
        Map<Object, Object> resMap = new HashedMap<>();
        //工厂创建产品属性对象
        DeviceBasePro devicePro = SimpleDeviceFactory.createDevicePro(productKey);
        //设置默认信息
        BeanUtil.copyProperties(deviceInfo, devicePro);
        if (devicePro == null) {
            return resMap;
        }
        // 对identify对应的属性进行赋值
        String lowerFirst = StrUtil.lowerFirst(deviceProperty.getIdentify());//小写首字母
        BeanUtil.setProperty(devicePro, StrUtil.toCamelCase(lowerFirst), deviceProperty.getValue());
        // 设置更改时间
        DateTime deviceTime = DateUtil.date(Long.valueOf(deviceProperty.getDeviceTime()) * 1000);
        devicePro.setDeviceTime(deviceTime);
        // 将对象转换成Map,并复制成新的map,解决hMset限定参数原因; 缓存key存在则忽略此次IOT未传递的数据,不存在不忽略
        Map<String, Object> stringObjectMap = BeanUtil.beanToMap(devicePro, false, exists);
        if (MapUtils.isNotEmpty(stringObjectMap)) {
            resMap.putAll(stringObjectMap);
        }
        return resMap;
    }

    /**
     * 保存设备属性上报数据
     *
     * @param deviceProperty 设备上报属性数据
     * @param deviceInfo     设备详细信息
     */
    private void saveDeviceData(DeviceProperty deviceProperty, PmgBatSmartEquipment deviceInfo) {
        // 实体类转换
        PmgBatDeviceData pmgBatDeviceData = new PmgBatDeviceData();
        BeanUtils.copyProperties(deviceProperty, pmgBatDeviceData);
        // 秒 转 毫秒 需要 * 1000
        Date date = DateUtil.date(Long.valueOf(deviceProperty.getDeviceTime()) * 1000);
        pmgBatDeviceData.setDeviceTime(date);
        pmgBatDeviceData.setValue(String.valueOf(deviceProperty.getValue()));
        // 插入设备详细信息
        pmgBatDeviceData.setTenantId(deviceInfo.getTenantId());
        pmgBatDeviceData.setParkId(deviceInfo.getParkId());
        pmgBatDeviceData.setCreateUser(deviceInfo.getCreateUser());
        pmgBatDeviceData.setCreateDept(deviceInfo.getCreateDept());
        parkDeviceDataService.save(pmgBatDeviceData);
    }
}