/*
 * 文 件 名:  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.MeterDevice;
import com.ruoyi.ems.model.Price;
import com.ruoyi.ems.service.IMeterDeviceService;
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 [产品/模块版本]
 */
public abstract class BaseMeterDevHandler extends BaseDevHandler {
    @Autowired
    protected IMeterDeviceService meterDeviceService;

    protected static final String LAST_HOUR_READING = "lastHourReading";

    /**
     * 获取计量设备列表
     *
     * @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) {
                ElecMeterH elecMeterH = meterHourProdSub(device);

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

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

        return cnt;
    }

    private ElecMeterH meterHourProdSub(MeterDevice device) {
        ElecMeterH elecMeterH = null;

        // 获取设备属性缓存
        Map<String, String> attrMap = attrCache.computeIfAbsent(device.getDeviceCode(), k -> new ConcurrentHashMap<>());

        String lastMeterReading = attrMap.get(LAST_HOUR_READING);
        String newMeterReading = attrMap.get("energy");

        // 缓存不为空，使用缓存抄表计算
        if (StringUtils.isNotEmpty(lastMeterReading)) {
            elecMeterH = execHourMeter(device, lastMeterReading, newMeterReading);
        }
        // 缓存为空，使用数据库记录计算
        else {
            ElecMeterH dbElecMeterH = elecMeterHService.selectLatelyItem(device.getDeviceCode());

            if (null != dbElecMeterH && null != dbElecMeterH.getMeterReading()) {
                elecMeterH = execHourMeter(device, String.valueOf(dbElecMeterH.getMeterReading()), newMeterReading);
            }
            else {
                // 无缓存也无数据库记录，则认为是首次上报
                if (StringUtils.isNotEmpty(newMeterReading)) {
                    updateCacheAfterSuccess(device.getDeviceCode(), LAST_HOUR_READING, newMeterReading);
                }
            }
        }

        return elecMeterH;
    }

    /**
     * 执行小时抄表计算
     *
     * @param device           设备信息
     * @param lastMeterReading 上次抄表值
     * @param newMeterReading  本次抄表值
     * @return
     */
    private ElecMeterH execHourMeter(MeterDevice device, String lastMeterReading, String newMeterReading) {
        ElecMeterH elecMeterH = null;
        BigDecimal lastEngValue = new BigDecimal(lastMeterReading);

        if (StringUtils.isNotEmpty(newMeterReading)) {
            BigDecimal newEngValue = new BigDecimal(newMeterReading);
            // 计算电量差值
            BigDecimal useQuantity = newEngValue.subtract(lastEngValue);
            // 倍率计算
            useQuantity = magnification(useQuantity, device.getMagnification());
            // 更新缓存
            updateCacheAfterSuccess(device.getDeviceCode(), LAST_HOUR_READING, newMeterReading);
            // 封装计量对象
            elecMeterH = buildElecMeterH(device, newEngValue, useQuantity);
        }
        else {
            BigDecimal useQuantity = BigDecimal.ZERO;
            // 封装计量对象
            elecMeterH = buildElecMeterH(device, lastEngValue, useQuantity);
        }

        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;
    }

    /**
     * 倍率计算
     *
     * @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      价格信息
     */
    private void completePrice(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());
        }
    }

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