package vip.xiaonuo.coldchain.core.alarm.service.messagepush;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import vip.xiaonuo.coldchain.core.alarm.bean.NotificationChannel;
import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarm;
import vip.xiaonuo.coldchain.core.alarm.bean.SensorAlarmUser;
import vip.xiaonuo.coldchain.core.alarm.mapper.SensorAlarmMapper;
import vip.xiaonuo.coldchain.core.config.JfcloudColdChainConstants;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Service
@Slf4j
@RequiredArgsConstructor
public class LocalSensorAlarmMessagePushService {
    private final Map<String, MessagePushService> pushServices;
    private final SensorAlarmMapper sensorAlarmMapper;
    // 用来记录每个用户对某设备和告警类型的推送历史，key 为 (openid, deviceID, alarmType)，value 为推送时间列表
    private final Map<String, List<Long>> userDeviceAlarmPushHistory = new ConcurrentHashMap<>();


    /**
     * 根据通知渠道推送消息，并更新通知状态
     *
     * @param alarm 告警信息
     */
    @Async("coldChainAsyncTask")
    public void pushAlarmMessage(SensorAlarm alarm) {
        String deviceID = alarm.getDeviceId(); // 获取设备ID
        String alarmType = alarm.getAlarmType(); // 获取告警类型
        List<String> openids = Optional.ofNullable(alarm.getAlarmUsers())
                .orElse(Collections.emptyList())
                .stream()
                .filter(Objects::nonNull)
                .map(SensorAlarmUser::getOpenId)
                .filter(Objects::nonNull)
                .toList();

        for (String openid : openids) {
            // 判断该用户对该设备和告警类型的推送是否超过限制
            if (hasExceededPushLimit(openid, deviceID, alarmType)) {
                log.info("用户 {} 对设备 {} 的告警类型 {} \n在过去{}分钟内推送次数超过限制，跳过推送", openid, deviceID, alarmType, JfcloudColdChainConstants.MESS_PUSH_TIME_WINDOW / 60 / 1000);
                continue;
            }
            // 遍历每个接收人和通知渠道，发送告警信息
            for (NotificationChannel channel : alarm.getNotificationChannel()) {
                MessagePushService pushService = pushServices.get(channel.name());
                if (pushService != null) {
                    try {
                        boolean b = pushService.sendAlarmMessage(alarm);
                        // 设置已通知状态
                        alarm.setNotified(b);
                    } catch (Exception e) {
                        log.error("推送消息失败，渠道: {}，错误: {}", channel.getChannelName(), e.getMessage());
                    }
                } else {
                    log.warn("不支持的通知渠道: {}", channel.getChannelName());
                }
            }
            // 记录推送时间
            recordPushTime(openid, deviceID, alarmType);
        }
        // 将告警信息存储到数据库
        saveAlarmToDatabase(alarm);
    }

    /**
     * 判断用户对某设备和某告警类型的推送是否超过了推送次数限制
     *
     * @param openid    用户的 openid
     * @param deviceID  设备ID
     * @param alarmType 告警类型
     * @return 是否超过限制
     */
    private boolean hasExceededPushLimit(String openid, String deviceID, String alarmType) {
        String key = generatePushHistoryKey(openid, deviceID, alarmType);
        List<Long> pushTimes = userDeviceAlarmPushHistory.get(key);
        if (pushTimes == null) {
            return false; // 没有记录，说明该用户、设备、告警类型组合没有推送记录
        }
        // 清理超过时间窗口的记录
        long currentTime = System.currentTimeMillis();
        pushTimes.removeIf(time -> currentTime - time > JfcloudColdChainConstants.MESS_PUSH_TIME_WINDOW);
        // 如果该用户对该设备和告警类型组合在当前时间窗口内已经超过最大推送次数，则不再推送
        return pushTimes.size() >= JfcloudColdChainConstants.MESS_PUSH_MAX_PUSH_COUNT;
    }

    /**
     * 生成唯一的推送历史记录的键值，基于用户 openid、设备ID和告警类型
     *
     * @param openid    用户的 openid
     * @param deviceID  设备ID
     * @param alarmType 告警类型
     * @return 唯一的键值
     */
    private String generatePushHistoryKey(String openid, String deviceID, String alarmType) {
        return openid + "_" + deviceID + "_" + alarmType;
    }

    /**
     * 记录用户对某设备和某告警类型的推送时间
     *
     * @param openid    用户的 openid
     * @param deviceID  设备ID
     * @param alarmType 告警类型
     */
    private void recordPushTime(String openid, String deviceID, String alarmType) {
        String key = generatePushHistoryKey(openid, deviceID, alarmType);
        List<Long> pushTimes = userDeviceAlarmPushHistory.computeIfAbsent(key, k -> new ArrayList<>());
        pushTimes.add(System.currentTimeMillis());
    }

    /**
     * 保存告警信息到数据库
     *
     * @param alarm 告警信息
     */
    private void saveAlarmToDatabase(SensorAlarm alarm) {
        try {
            sensorAlarmMapper.insert(alarm);
        } catch (Exception e) {
            log.error("保存告警信息到数据库失败: {}", e.getMessage());
        }
    }
}
