/*
 * 文 件 名:  MeterDevHandler
 * 版    权:  华设设计集团股份有限公司
 * 描    述:  <描述>
 * 修 改 人:  lvwenbin
 * 修改时间:  2025/4/15
 * 跟踪单号:  <跟踪单号>
 * 修改单号:  <修改单号>
 * 修改内容:  <修改内容>
 */
package com.ruoyi.ems.handle;

import com.huashe.common.utils.DateUtils;
import com.ruoyi.ems.domain.ElecMeterH;
import com.ruoyi.ems.domain.FdEnergyPriceConfig;
import com.ruoyi.ems.domain.MeterDevice;
import com.ruoyi.ems.domain.WaterMeterH;
import com.ruoyi.ems.model.Price;
import com.ruoyi.ems.service.IMeterDeviceService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 计量设备处理类
 * <功能详细描述>
 *
 * @author lvwenbin
 * @version [版本号, 2025/4/15]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Slf4j
public abstract class BaseMeterDevHandler extends BaseDevHandler {
    @Autowired
    protected IMeterDeviceService meterDeviceService;

    protected static final String LAST_HOUR_READING = "LAST_HOUR_READING";

    protected static final String NEW_HOUR_READING = "NEW_HOUR_READING";

    protected static final String LAST_HOUR_TIMESTAMP = "LAST_HOUR_TIMESTAMP";

    protected static final long MAX_CACHE_AGE_MILLIS = 2 * 60 * 60 * 1000;


    /**
     * 获取计量设备列表
     *
     * @return 设备列表
     */
    public abstract List<MeterDevice> getMeterDeviceList();

    /**
     * 产出小时电量计量
     */
    public int meterHourProd() {
        int cnt = 0;

        try {
            List<MeterDevice> deviceList = getMeterDeviceList();
            List<ElecMeterH> meterHList = new ArrayList<>();
            Map<String, Price> priceMap = new ConcurrentHashMap<>();
            Date date = DateUtils.adjustHour(new Date(), -1);

            for (MeterDevice device : deviceList) {
                // 读取历史抄表
                String lastMeterReading = redisService.getCacheMapValue(device.getDeviceCode(), LAST_HOUR_READING);

                // 读取最新抄表值
                String newMeterReading = redisService.getCacheMapValue(device.getDeviceCode(), NEW_HOUR_READING);

                ElecMeterH elecMeterH = getElecMeterH(device, lastMeterReading, newMeterReading);

                if (null != elecMeterH) {
                    Price price = priceMap.computeIfAbsent(device.getAreaCode(),
                        k -> priceService.getElecHourPrice(device.getAreaCode(), date));
                    completeElecPrice(elecMeterH, price);
                    meterHList.add(elecMeterH);
                }
            }

            if (CollectionUtils.isNotEmpty(meterHList)) {
                cnt = elecMeterHService.insertBatch(meterHList);
            }
        }
        catch (Exception e) {
            log.error("定时产出小时电量计量失败!", e);
        }

        return cnt;
    }

    public ElecMeterH getElecMeterH(MeterDevice mDevice, String lastMeterReading, String newMeterReading) {
        return getElecMeterH(mDevice.getDeviceCode(), mDevice, lastMeterReading, newMeterReading);
    }

    public ElecMeterH getElecMeterH(String deviceCode, MeterDevice mDevice, String lastMeterReading,
        String newMeterReading) {
        ElecMeterH elecMeterH = null;

        // 缓存不为空，使用缓存抄表计算
        if (StringUtils.isNotEmpty(lastMeterReading)) {
            elecMeterH = execElecHourMeter(mDevice, lastMeterReading, newMeterReading);
            // 本周期抄表完成，将新抄表值覆盖作为下个周期的起始
            updateCacheAfterSuccess(deviceCode, LAST_HOUR_READING, newMeterReading);
        }
        // 缓存为空，使用数据库记录计算
        else {
            ElecMeterH dbElecMeterH = elecMeterHService.selectLatelyItem(deviceCode);

            if (null != dbElecMeterH && null != dbElecMeterH.getMeterReading()) {
                elecMeterH = execElecHourMeter(mDevice, String.valueOf(dbElecMeterH.getMeterReading()),
                    newMeterReading);
                // 本周期抄表完成，将新抄表值覆盖作为下个周期的起始
                updateCacheAfterSuccess(deviceCode, LAST_HOUR_READING, newMeterReading);
            }
            else {
                // 无缓存也无数据库记录，则认为是首次上报
                if (StringUtils.isNotEmpty(newMeterReading)) {
                    updateCacheAfterSuccess(deviceCode, LAST_HOUR_READING, newMeterReading);
                }
            }
        }

        return elecMeterH;
    }

    /**
     * 执行小时抄表计算
     *
     * @param mDevice          计量设备
     * @param lastMeterReading 上次抄表值
     * @param newMeterReading  本次抄表值
     * @return
     */
    protected ElecMeterH execElecHourMeter(MeterDevice mDevice, String lastMeterReading, String newMeterReading) {
        ElecMeterH elecMeterH = null;

        if (StringUtils.isEmpty(newMeterReading) || StringUtils.isEmpty(lastMeterReading)) {
            log.warn("电表读数为空 - 设备:{}, 新值:{}, 旧值:{}",
                mDevice.getDeviceCode(), newMeterReading, lastMeterReading);
            return null;
        }

        try {
            BigDecimal lastEngValue = new BigDecimal(lastMeterReading);
            BigDecimal newEngValue = new BigDecimal(newMeterReading);

            // 计算电量差值
            BigDecimal useQuantity = newEngValue.subtract(lastEngValue);

            // 数据合理性校验
            if (useQuantity.doubleValue() < 0) {
                // 处理表计重置的情况
                if (newEngValue.doubleValue() < 100 && lastEngValue.doubleValue() > 10000) {
                    log.info("检测到电表可能重置 - 设备:{}, 新值:{}, 旧值:{}",
                        mDevice.getDeviceCode(), newEngValue, lastEngValue);
                    // 表计重置时，本次不产生数据
                    return null;
                } else {
                    log.error("检测到电表异常负值 - 设备:{}, 新值:{}, 旧值:{}, 差值:{}",
                        mDevice.getDeviceCode(), newEngValue, lastEngValue, useQuantity);
                    return null;
                }
            }

            // 检查是否增长过快（每小时最大1000度）
            if (useQuantity.doubleValue() > 1000.0) {
                log.warn("电表用量增长异常 - 设备:{}, 小时用量:{}度, 新值:{}, 旧值:{}",
                    mDevice.getDeviceCode(), useQuantity, newEngValue, lastEngValue);
            }

            // 倍率计算
            useQuantity = magnification(useQuantity, mDevice.getMagnification());
            // 封装计量对象
            elecMeterH = buildElecMeterH(mDevice, newEngValue, useQuantity);

        } catch (NumberFormatException e) {
            log.error("电表读数格式错误 - 设备:{}, 新值:{}, 旧值:{}",
                mDevice.getDeviceCode(), newMeterReading, lastMeterReading, e);
        }

        return elecMeterH;
    }

    /**
     * 封装计量对象
     *
     * @param device      设备信息
     * @param newEngValue 最新电量值
     * @param useQuantity 用量差值
     * @return ElecMeterH 计量对象
     */
    private ElecMeterH buildElecMeterH(MeterDevice device, BigDecimal newEngValue, BigDecimal useQuantity) {
        Date date = DateUtils.adjustHour(new Date(), -1);
        ElecMeterH elecMeterH = new ElecMeterH();
        elecMeterH.setAreaCode(device.getAreaCode());
        elecMeterH.setDeviceCode(device.getDeviceCode());
        elecMeterH.setRecordTime(date);
        elecMeterH.setDate(date);
        elecMeterH.setTime(date);
        elecMeterH.setTimeIndex(getHourIndex(date));
        elecMeterH.setElecQuantity(useQuantity.doubleValue());
        elecMeterH.setMeterReading(newEngValue.doubleValue());

        return elecMeterH;
    }

    public WaterMeterH getWaterMeterH(MeterDevice mDevice, String lastMeterReading, String newMeterReading) {
        return getWaterMeterH(mDevice.getDeviceCode(), mDevice, lastMeterReading, newMeterReading);
    }

    public WaterMeterH getWaterMeterH(String deviceCode, MeterDevice mDevice, String lastMeterReading,
        String newMeterReading) {
        WaterMeterH waterMeterH = null;

        // 缓存不为空，使用缓存抄表计算
        if (StringUtils.isNotEmpty(lastMeterReading)) {
            waterMeterH = execWaterHourMeter(mDevice, lastMeterReading, newMeterReading);

            // 更新缓存
            updateCacheAfterSuccess(deviceCode, LAST_HOUR_READING, newMeterReading);
        }
        // 缓存为空，使用数据库记录计算
        else {
            WaterMeterH dbWaterMeterH = waterMeterHService.selectLatelyItem(mDevice.getDeviceCode());

            if (null != dbWaterMeterH && null != dbWaterMeterH.getMeterReading()) {
                waterMeterH = execWaterHourMeter(mDevice, String.valueOf(dbWaterMeterH.getMeterReading()),
                    newMeterReading);

                // 更新缓存
                updateCacheAfterSuccess(deviceCode, LAST_HOUR_READING, newMeterReading);
            }
            else {
                // 无缓存也无数据库记录，则认为是首次上报
                if (StringUtils.isNotEmpty(newMeterReading)) {
                    updateCacheAfterSuccess(deviceCode, LAST_HOUR_READING, newMeterReading);
                }
            }
        }

        return waterMeterH;
    }

    /**
     * 执行小时抄表计算
     *
     * @param device           设备信息
     * @param lastMeterReading 上次抄表值
     * @param newMeterReading  本次抄表值
     * @return
     */
    protected WaterMeterH execWaterHourMeter(MeterDevice device, String lastMeterReading, String newMeterReading) {
        WaterMeterH waterMeterH = null;

        if (StringUtils.isEmpty(newMeterReading) || StringUtils.isEmpty(lastMeterReading)) {
            log.warn("水表读数为空 - 设备:{}, 新值:{}, 旧值:{}",
                device.getDeviceCode(), newMeterReading, lastMeterReading);
            return null;
        }

        try {
            BigDecimal lastEngValue = new BigDecimal(lastMeterReading);
            BigDecimal newEngValue = new BigDecimal(newMeterReading);

            // 计算水量差值
            BigDecimal useQuantity = newEngValue.subtract(lastEngValue);

            // 数据合理性校验
            if (useQuantity.doubleValue() < 0) {
                // 处理表计重置的情况
                if (newEngValue.doubleValue() < 10 && lastEngValue.doubleValue() > 1000) {
                    log.info("检测到水表可能重置 - 设备:{}, 新值:{}, 旧值:{}",
                        device.getDeviceCode(), newEngValue, lastEngValue);
                    // 表计重置时，本次不产生数据
                    return null;
                } else {
                    log.error("检测到水表异常负值 - 设备:{}, 新值:{}, 旧值:{}, 差值:{}",
                        device.getDeviceCode(), newEngValue, lastEngValue, useQuantity);
                    return null;
                }
            }

            // 检查是否增长过快（每小时最大100吨）
            if (useQuantity.doubleValue() > 100.0) {
                log.warn("水表用量增长异常 - 设备:{}, 小时用量:{}吨, 新值:{}, 旧值:{}",
                    device.getDeviceCode(), useQuantity, newEngValue, lastEngValue);
            }

            // 倍率计算
            useQuantity = magnification(useQuantity, device.getMagnification());
            // 封装计量对象
            waterMeterH = buildWaterMeterH(device, newEngValue, useQuantity);

        } catch (NumberFormatException e) {
            log.error("水表读数格式错误 - 设备:{}, 新值:{}, 旧值:{}",
                device.getDeviceCode(), newMeterReading, lastMeterReading, e);
        }

        return waterMeterH;
    }

    /**
     * 封装计量对象
     *
     * @param device      设备信息
     * @param newEngValue 最新电量值
     * @param useQuantity 用量差值
     * @return ElecMeterH 计量对象
     */
    private WaterMeterH buildWaterMeterH(MeterDevice device, BigDecimal newEngValue, BigDecimal useQuantity) {
        Date date = DateUtils.adjustHour(new Date(), -1);
        WaterMeterH waterMeterH = new WaterMeterH();
        waterMeterH.setAreaCode(device.getAreaCode());
        waterMeterH.setDeviceCode(device.getDeviceCode());
        waterMeterH.setRecordTime(date);
        waterMeterH.setDate(date);
        waterMeterH.setTime(date);
        waterMeterH.setTimeIndex(getHourIndex(date));
        waterMeterH.setWaterQuantity(useQuantity.doubleValue());
        waterMeterH.setMeterReading(newEngValue.doubleValue());

        return waterMeterH;
    }

    /**
     * 倍率计算
     *
     * @param quantity      原始用量
     * @param magnification 倍率
     * @return
     */
    private BigDecimal magnification(BigDecimal quantity, Integer magnification) {
        return null != magnification ? quantity.multiply(new BigDecimal(String.valueOf(magnification))) : quantity;
    }

    /**
     * 完善价格信息
     *
     * @param elecMeterH 计量对象
     * @param price      价格信息
     */
    public void completeElecPrice(ElecMeterH elecMeterH, Price price) {
        if (null != price) {
            BigDecimal useQuantity = new BigDecimal(String.valueOf(elecMeterH.getElecQuantity()));
            BigDecimal cost = useQuantity.multiply(new BigDecimal(String.valueOf(price.getPriceValue())));
            elecMeterH.setMeterType(price.getMeterType());
            elecMeterH.setMeterUnitPrice(price.getPriceValue());
            elecMeterH.setUseElecCost(cost.doubleValue());
        }
    }

    /**
     * 完善价格信息
     *
     * @param waterMeterH 计量对象
     * @param config      价格信息
     */
    public void completeWaterPrice(WaterMeterH waterMeterH, FdEnergyPriceConfig config) {
        if (null != config) {
            BigDecimal useQuantity = new BigDecimal(String.valueOf(waterMeterH.getWaterQuantity()));
            BigDecimal cost = useQuantity.multiply(new BigDecimal(String.valueOf(config.getUnitPrice())));
            waterMeterH.setMeterUnitPrice(config.getUnitPrice());
            waterMeterH.setUseWaterCost(cost.doubleValue());
        }
    }

    protected static int getHourIndex(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        return hour + 1;
    }

    /**
     * 格式化数值为指定小数位数
     *
     * @param value 原始值
     * @param decimalPlaces 小数位数
     * @return 格式化后的值
     */
    protected double formatToDecimalPlaces(double value, int decimalPlaces) {
        return Math.round(value * Math.pow(10, decimalPlaces)) / Math.pow(10, decimalPlaces);
    }
}