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

import com.alibaba.fastjson2.JSONObject;
import com.huashe.common.utils.DateUtils;
import com.ruoyi.ems.domain.EmsDevice;
import com.ruoyi.ems.domain.EmsObjAbilityCallLog;
import com.ruoyi.ems.domain.EmsObjAttrValue;
import com.ruoyi.ems.domain.EmsObjEventLog;
import com.ruoyi.ems.domain.EmsObjReportLog;
import com.ruoyi.ems.enums.DevOnlineStatus;
import com.ruoyi.ems.model.AbilityPayload;
import com.ruoyi.ems.model.CallResponse;
import com.ruoyi.ems.model.MqttCacheMsg;
import com.ruoyi.ems.service.IElecMeterHService;
import com.ruoyi.ems.service.IEmsDeviceService;
import com.ruoyi.ems.service.IEmsObjAbilityCallLogService;
import com.ruoyi.ems.service.IEmsObjAttrValueService;
import com.ruoyi.ems.service.IEmsObjEventLogService;
import com.ruoyi.ems.service.IEmsObjReportLogService;
import com.ruoyi.ems.service.IPriceService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

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

    @Autowired
    protected IEmsObjAttrValueService objAttrValueService;

    @Autowired
    protected IElecMeterHService elecMeterHService;

    @Autowired
    protected IPriceService priceService;

    @Autowired
    protected IEmsObjAbilityCallLogService objAbilityCallLogService;

    @Autowired
    protected IEmsObjReportLogService objReportLogService;

    @Autowired
    protected IEmsObjEventLogService objEventLogService;

    /**
     * 抽象方法，用于能力调用
     *
     * @param abilityParam 能力参数
     * @return 处理结果字符串
     */
    public abstract CallResponse<Void> call(AbilityPayload abilityParam);

    /**
     * 获取设备列表
     *
     * @return 设备列表
     */
    public abstract List<EmsDevice> getDeviceList();

    /**
     * 缓存设备属性
     */
    protected static final Map<String, Map<String, String>> attrCache = new ConcurrentHashMap<>();

    /**
     * 最后一次消息时间缓存key
     */
    protected static final String MQTT_LAST_TIME = "mqttLastMsgTime";

    /**
     * 更新最后消息时
     *
     * @param device    设备
     * @param timeValue 时间值
     */
    public void updateMsgTime(EmsDevice device, Date timeValue) {
        try {
            String dateTime = DateUtils.dateToString(timeValue, DateUtils.YYYY_MM_DD_HH_MM_SS);

            attrCache.computeIfAbsent(device.getDeviceCode(), k -> new ConcurrentHashMap<>());

            // 更新缓存
            updateCacheAfterSuccess(device.getDeviceCode(), MQTT_LAST_TIME, dateTime);
        }
        catch (Exception e) {
            log.error("刷新缓存消息时间失败!", e);
        }
    }

    /**
     * 定时刷新在线状态
     * <br/>每小时执行一次，扫描2个小时无消息的设备，标记为离线状态
     */
    public void refreshOnline(long threshold) {
        long currentTime = new Date().getTime();

        List<EmsDevice> deviceList = getDeviceList();

        if (CollectionUtils.isNotEmpty(deviceList)) {
            for (EmsDevice device : deviceList) {
                Map<String, String> attrMap = attrCache.get(device.getDeviceCode());

                if (MapUtils.isNotEmpty(attrMap) && null != attrMap.get(MQTT_LAST_TIME)) {
                    String lastMsgTime = attrMap.get(MQTT_LAST_TIME);

                    // 计算最后一次消息至今的时间差
                    long time =
                        currentTime - DateUtils.stringToDate(lastMsgTime, DateUtils.YYYY_MM_DD_HH_MM_SS).getTime();

                    if (time > threshold) {
                        refreshStatus(device, DevOnlineStatus.OFFLINE);
                    }
                }
            }
        }
    }

    /**
     * 校验是否需要更新属性值
     *
     * @param jsonBody jsonBody 消息体
     * @param attrKey  属性key
     * @return true 更新，false 不更新
     */
    public boolean updateAttr(EmsDevice device, JSONObject jsonBody, String attrKey) {
        boolean flag = false;

        try {
            // 校验消息体是否包含属性key
            if (!jsonBody.containsKey(attrKey)) {
                return flag;
            }

            // 获取属性值
            String currentValue = jsonBody.getString(attrKey);

            // 获取缓存
            Map<String, String> attrMap = attrCache.computeIfAbsent(device.getDeviceCode(), k -> {
                List<EmsObjAttrValue> attrList = objAttrValueService.selectByObjCode(device.getDeviceModel(), k);

                if (CollectionUtils.isNotEmpty(attrList)) {
                    return attrList.stream().collect(
                        Collectors.toMap(EmsObjAttrValue::getAttrKey, EmsObjAttrValue::getAttrValue, (v1, v2) -> v1,
                            ConcurrentHashMap::new));
                }
                else {
                    return new ConcurrentHashMap<>();
                }
            });

            // 从缓存中获取属性值
            String cacheValue = attrMap.get(attrKey);

            // 比较属性值是否发生变化
            if (!StringUtils.equals(currentValue, cacheValue)) {
                EmsObjAttrValue attrValue = new EmsObjAttrValue(device.getDeviceCode(), device.getDeviceModel(),
                    attrKey, currentValue);
                // 更新数据库
                objAttrValueService.mergeObjAttrValue(attrValue);
                // 更新缓存
                updateCacheAfterSuccess(device.getDeviceCode(), attrKey, currentValue);

                flag = true;
            }
        }
        catch (Exception e) {
            log.error("检查属性并更新失败!", e);
        }

        return flag;
    }

    /**
     * 更新缓存值
     *
     * @param deviceCode 设备信息
     * @param attrKey    属性值
     * @param newValue
     */
    public void updateCacheAfterSuccess(String deviceCode, String attrKey, String newValue) {
        attrCache.computeIfPresent(deviceCode, (k, attrMap) -> {
            attrMap.put(attrKey, newValue);
            return attrMap;
        });
    }

    /**
     * 刷新设备状态
     *
     * @param device 设备信息
     */
    public void refreshStatus(EmsDevice device, DevOnlineStatus status) {
        if (null == device.getDeviceStatus() || device.getDeviceStatus() != status.getCode()) {
            device.setDeviceStatus(status.getCode());
            deviceService.updateEmsDevice(device);
            triggerEvent(device, status == DevOnlineStatus.ONLINE ? "online" : "offline", null, new Date());
            log.debug("设备[{}]{}...", device.getDeviceCode(), status == DevOnlineStatus.ONLINE ? "上线" : "下线");
        }
    }

    /**
     * 触发设备事件
     *
     * @param device   设备信息
     * @param eventKey 事件标记
     * @param detail   事件详情
     * @param date     时间戳
     */
    public void triggerEvent(EmsDevice device, String eventKey, String detail, Date date) {
        EmsObjEventLog eventLog = new EmsObjEventLog();
        eventLog.setObjCode(device.getDeviceCode());
        eventLog.setModelCode(device.getDeviceModel());
        eventLog.setEventKey(eventKey);
        eventLog.setEventTime(date);
        eventLog.setEventDetail(detail);
        objEventLogService.insertLog(eventLog);
    }



    protected EmsObjAbilityCallLog saveCallLog(AbilityPayload abilityParam, long sendTime, int callStatus) {
        EmsObjAbilityCallLog objAbilityCallLog = new EmsObjAbilityCallLog();
        objAbilityCallLog.setObjCode(abilityParam.getObjCode());
        objAbilityCallLog.setModelCode(abilityParam.getModeCode());
        objAbilityCallLog.setAbilityKey(abilityParam.getAbilityKey());
        objAbilityCallLog.setCallTime(new Date(sendTime));
        objAbilityCallLog.setCallPayload(abilityParam.getAbilityParam());
        objAbilityCallLog.setCallStatus(callStatus);
        objAbilityCallLogService.addLog(objAbilityCallLog);
        return objAbilityCallLog;
    }

    protected void updateCallLog(EmsObjAbilityCallLog objAbilityCallLog, MqttCacheMsg cacheMsg, int callStatus) {
        objAbilityCallLog.setResTime(cacheMsg.getReceiveTime());
        objAbilityCallLog.setResPayload(cacheMsg.getPayload());
        objAbilityCallLog.setCallStatus(callStatus);
        objAbilityCallLogService.updateLog(objAbilityCallLog);
    }

    protected void saveReportLog(String objCode, String modelCode, String msgDesc, String payload) {
        EmsObjReportLog objReportLog = new EmsObjReportLog();
        objReportLog.setObjCode(objCode);
        objReportLog.setModelCode(modelCode);
        objReportLog.setMsgDesc(msgDesc);
        objReportLog.setReportPayload(payload);
        objReportLog.setReportTime(new Date());

        objReportLogService.insertLog(objReportLog);
    }
}