package com.genersoft.iot.vmp.storager.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.common.ServerInfo;
import com.genersoft.iot.vmp.common.SystemAllInfo;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.dao.DeviceChannelMapper;
import com.genersoft.iot.vmp.gb28181.dao.DeviceMapper;
import com.genersoft.iot.vmp.media.bean.MediaInfo;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.utils.JsonUtil;
import com.genersoft.iot.vmp.utils.SystemInfoUtils;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.jspecify.annotations.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.*;

@SuppressWarnings("rawtypes")
@Slf4j
@Component
public class RedisCatchStorageImpl implements IRedisCatchStorage {


    @Autowired
    private DeviceChannelMapper deviceChannelMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private UserSetting userSetting;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private RedisTemplate<String, Long> longRedisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public List<SendRtpInfo> queryAllSendRTPServer() {
        return Collections.emptyList();
    }

    @Override
    public Long getCSEQ() {
        String key = VideoManagerConstants.SIP_CSEQ_PREFIX  + userSetting.getServerId();

        Long result =  redisTemplate.opsForValue().increment(key, 1L);
        if (result != null && result > Integer.MAX_VALUE) {
            redisTemplate.opsForValue().set(key, 1);
            result = 1L;
        }
        return result;
    }

    @Override
    public void resetAllCSEQ() {
        String key = VideoManagerConstants.SIP_CSEQ_PREFIX  + userSetting.getServerId();
        redisTemplate.opsForValue().set(key, 1);
    }


    @Override
    public void updateWVPInfo(ServerInfo serverInfo, int time) {
        String key = VideoManagerConstants.WVP_SERVER_PREFIX + userSetting.getServerId();
        Duration duration = Duration.ofSeconds(time);
        redisTemplate.opsForValue().set(key, serverInfo, duration);
        // 设置平台的分数值
        String setKey = VideoManagerConstants.WVP_SERVER_LIST;
        // 首次设置就设置为0, 后续值越小说明越是最近启动的
        redisTemplate.opsForZSet().add(setKey, userSetting.getServerId(), System.currentTimeMillis());
    }

    @Override
    public void removeOfflineWVPInfo(String serverId) {
        String setKey = VideoManagerConstants.WVP_SERVER_LIST;
        // 首次设置就设置为0, 后续值越小说明越是最近启动的
        redisTemplate.opsForZSet().remove(setKey, serverId);
    }

    @Override
    public void sendStreamChangeMsg(String type, JSONObject jsonObject) {
        String key = VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + type;
        log.info("[redis 流变化事件] 发送 {}: {}", key, jsonObject.toString());
        redisTemplate.convertAndSend(key, jsonObject);
    }

    @Override
    public void addStream(MediaServer mediaServerItem, String type, String app, String streamId, MediaInfo mediaInfo) {
        // 查找是否使用了callID
        StreamAuthorityInfo streamAuthorityInfo = getStreamAuthorityInfo(app, streamId);
        String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX  + userSetting.getServerId() + "_" + type.toUpperCase() + "_" + app + "_" + streamId + "_" + mediaServerItem.getId();
        if (streamAuthorityInfo != null) {
            mediaInfo.setCallId(streamAuthorityInfo.getCallId());
        }
        redisTemplate.opsForValue().set(key, JSON.toJSONString(mediaInfo));
    }

    @Override
    public void removeStream(String mediaServerId, String type, String app, String streamId) {
        String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type.toUpperCase() + "_"  + app + "_" + streamId + "_" + mediaServerId;
        redisTemplate.delete(key);
    }

    @Override
    public void removeStream(String mediaServerId, String type) {
        String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type.toUpperCase() + "_*_*_" + mediaServerId;
        List<Object> streams = RedisUtil.scan(redisTemplate, key);
        for (Object stream : streams) {
            redisTemplate.delete(stream);
        }
    }

    @Override
    public List<MediaInfo> getStreams(String mediaServerId, String type) {
        List<MediaInfo> result = new ArrayList<>();
        String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type.toUpperCase() + "_*_*_" + mediaServerId;
        List<Object> streams = RedisUtil.scan(redisTemplate, key);
        for (Object stream : streams) {
            String mediaInfoJson = (String)redisTemplate.opsForValue().get(stream);
            MediaInfo mediaInfo = JSON.parseObject(mediaInfoJson, MediaInfo.class);
            result.add(mediaInfo);
        }
        return result;
    }

    @Override
    public void updateDevice(Device device) {
        String key = VideoManagerConstants.DEVICE_PREFIX;
        redisTemplate.opsForHash().put(key, device.getDeviceId(), device);
    }

    @Override
    public void removeDevice(String deviceId) {
        String key = VideoManagerConstants.DEVICE_PREFIX;
        redisTemplate.opsForHash().delete(key, deviceId);
    }

    @Override
    public void removeAllDevice() {
        String key = VideoManagerConstants.DEVICE_PREFIX;
        redisTemplate.delete(key);
    }

    @Override
    public List<Device> getAllDevices() {
        String key = VideoManagerConstants.DEVICE_PREFIX;
        List<Device> result = new ArrayList<>();
        List<Object> values = redisTemplate.opsForHash().values(key);
        for (Object value : values) {
            if (Objects.nonNull(value)) {
                result.add((Device)value);
            }
        }
        return result;
    }

    @Override
    public Device getDevice(String deviceId) {
        String key = VideoManagerConstants.DEVICE_PREFIX;
        Device device;
        Object object = redisTemplate.opsForHash().get(key, deviceId);
        if (object == null){
            device = deviceMapper.getDeviceByDeviceId(deviceId);
            if (device != null) {
                updateDevice(device);
            }
        }else {
            device = (Device)object;
        }
        return device;
    }

    @Override
    public List<Device> getDeviceList(Set<String> deviceIds) {
        String key = VideoManagerConstants.DEVICE_PREFIX;
        List<Device> deviceList  = new ArrayList<>();
        List<Object> objectList = redisTemplate.opsForHash().multiGet(key, Arrays.asList(deviceIds.toArray()));
        for (Object object : objectList) {
            deviceList.add((Device)object);
        }
        return deviceList;
    }

    @Override
    public void updateGpsMsgInfo(GPSMsgInfo gpsMsgInfo) {
        String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId();
        Duration duration = Duration.ofSeconds(60L);
        gpsMsgInfo.setStored(false);
        redisTemplate.opsForHash().put(key, gpsMsgInfo.getId(),gpsMsgInfo);
        redisTemplate.expire(key, duration);
        // 默认GPS消息保存1分钟
    }

    @Override
    public GPSMsgInfo getGpsMsgInfo(String channelId) {
        String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId();
        return (GPSMsgInfo) redisTemplate.opsForHash().get(key, channelId);
    }

    @Override
    public List<GPSMsgInfo> getAllGpsMsgInfo() {
        String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId();
        List<GPSMsgInfo> result = new ArrayList<>();
        List<Object> values = redisTemplate.opsForHash().values(key);
        for (Object value : values) {
            result.add((GPSMsgInfo)value);
        }
        return result;
    }

    @Override
    public void updateStreamAuthorityInfo(String app, String stream, StreamAuthorityInfo streamAuthorityInfo) {
        String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY;
        String objectKey = app+ "_" + stream;
        redisTemplate.opsForHash().put(key, objectKey, streamAuthorityInfo);
    }

    @Override
    public void removeStreamAuthorityInfo(String app, String stream) {
        String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY;
        String objectKey = app+ "_" + stream;
        redisTemplate.opsForHash().delete(key, objectKey);
    }

    @Override
    public StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream) {
        String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY;
        String objectKey = app+ "_" + stream;
        return (StreamAuthorityInfo)redisTemplate.opsForHash().get(key, objectKey);

    }

    @Override
    public List<StreamAuthorityInfo> getAllStreamAuthorityInfo() {
        String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY;
        List<StreamAuthorityInfo> result = new ArrayList<>();
        List<Object> values = redisTemplate.opsForHash().values(key);
        for (Object value : values) {
            result.add((StreamAuthorityInfo)value);
        }
        return result;
    }


    @Override
    public MediaInfo getStreamInfo(String app, String streamId, String mediaServerId) {
        String scanKey = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX  + userSetting.getServerId() + "_*_" + app + "_" + streamId + "_" + mediaServerId;

        MediaInfo result = null;
        List<Object> keys = RedisUtil.scan(redisTemplate, scanKey);
        if (keys.size() > 0) {
            String key = (String) keys.get(0);
            String mediaInfoJson = (String)redisTemplate.opsForValue().get(key);
            result = JSON.parseObject(mediaInfoJson, MediaInfo.class);
        }

        return result;
    }

    @Override
    public MediaInfo getProxyStream(String app, String streamId) {
        String scanKey = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX  + userSetting.getServerId() + "_PULL_" + app + "_" + streamId + "_*";

        MediaInfo result = null;
        List<Object> keys = RedisUtil.scan(redisTemplate, scanKey);
        if (keys.size() > 0) {
            String key = (String) keys.get(0);
            String mediaInfoJson = (String)redisTemplate.opsForValue().get(key);
            result = JSON.parseObject(mediaInfoJson, MediaInfo.class);
        }

        return result;
    }

    @Override
    public void addCpuInfo(double cpuInfo) {
        String key = VideoManagerConstants.SYSTEM_INFO_CPU_PREFIX + userSetting.getServerId();
        Map<String, String> infoMap = new HashMap<>();
        infoMap.put("time", DateUtil.getNow());
        infoMap.put("data", String.valueOf(cpuInfo));
        redisTemplate.opsForList().rightPush(key, infoMap);
        // 每秒一个，最多只存30个
        Long size = redisTemplate.opsForList().size(key);
        if (size != null && size >= 30) {
            for (int i = 0; i < size - 30; i++) {
                redisTemplate.opsForList().leftPop(key);
            }
        }
    }

    @Override
    public void addMemInfo(double memInfo) {
        String key = VideoManagerConstants.SYSTEM_INFO_MEM_PREFIX + userSetting.getServerId();
        Map<String, String> infoMap = new HashMap<>();
        infoMap.put("time", DateUtil.getNow());
        infoMap.put("data", String.valueOf(memInfo));
        redisTemplate.opsForList().rightPush(key, infoMap);
        // 每秒一个，最多只存30个
        Long size = redisTemplate.opsForList().size(key);
        if (size != null && size >= 30) {
            for (int i = 0; i < size - 30; i++) {
                redisTemplate.opsForList().leftPop(key);
            }
        }
    }

    @Override
    public void addNetInfo(Map<String, Double> networkInterfaces) {
        String key = VideoManagerConstants.SYSTEM_INFO_NET_PREFIX + userSetting.getServerId();
        Map<String, Object> infoMap = new HashMap<>();
        infoMap.put("time", DateUtil.getNow());
        for (String netKey : networkInterfaces.keySet()) {
            infoMap.put(netKey, networkInterfaces.get(netKey));
        }
        redisTemplate.opsForList().rightPush(key, infoMap);
        // 每秒一个，最多只存30个
        Long size = redisTemplate.opsForList().size(key);
        if (size != null && size >= 30) {
            for (int i = 0; i < size - 30; i++) {
                redisTemplate.opsForList().leftPop(key);
            }
        }
    }

    @Override
    public void addDiskInfo(List<Map<String, Object>> diskInfo) {

        String key = VideoManagerConstants.SYSTEM_INFO_DISK_PREFIX + userSetting.getServerId();
        redisTemplate.opsForValue().set(key, diskInfo);
    }

    @Override
    public SystemAllInfo getSystemInfo() {
        String cpuKey = VideoManagerConstants.SYSTEM_INFO_CPU_PREFIX + userSetting.getServerId();
        String memKey = VideoManagerConstants.SYSTEM_INFO_MEM_PREFIX + userSetting.getServerId();
        String netKey = VideoManagerConstants.SYSTEM_INFO_NET_PREFIX + userSetting.getServerId();
        String diskKey = VideoManagerConstants.SYSTEM_INFO_DISK_PREFIX + userSetting.getServerId();
        SystemAllInfo systemAllInfo = new SystemAllInfo();
        systemAllInfo.setCpu(redisTemplate.opsForList().range(cpuKey, 0, -1));
        systemAllInfo.setMem(redisTemplate.opsForList().range(memKey, 0, -1));
        systemAllInfo.setNet(redisTemplate.opsForList().range(netKey, 0, -1));

        systemAllInfo.setDisk(redisTemplate.opsForValue().get(diskKey));
        systemAllInfo.setNetTotal(SystemInfoUtils.getNetworkTotal());
        return systemAllInfo;
    }

    @Override
    public void sendStreamPushRequestedMsg(MessageForPushChannel msg) {
        String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED;
        log.info("[redis发送通知] 发送 推流被请求 {}: {}/{}", key, msg.getApp(), msg.getStream());
        redisTemplate.convertAndSend(key, JSON.toJSON(msg));
    }

    @Override
    public void sendAlarmMsg(AlarmChannelMessage msg) {
        // 此消息用于对接第三方服务下级来的消息内容
        String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM;
        log.info("[redis发送通知] 发送 报警{}: {}", key, JSON.toJSON(msg));
        redisTemplate.convertAndSend(key, JSON.toJSON(msg));
    }

    @Override
    public boolean deviceIsOnline(String deviceId) {
        return getDevice(deviceId).isOnLine();
    }


    @Override
    public void sendStreamPushRequestedMsgForStatus() {
        String key = VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED;
        log.info("[redis通知] 发送 获取所有推流设备的状态");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put(key, key);
        redisTemplate.convertAndSend(key, jsonObject);
    }

    @Override
    public int getPushStreamCount(String id) {
        String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_PUSH_*_*_" + id;
        return RedisUtil.scan(redisTemplate, key).size();
    }

    @Override
    public int getProxyStreamCount(String id) {
        String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_PULL_*_*_" + id;
        return RedisUtil.scan(redisTemplate, key).size();
    }

    @Override
    public int getGbSendCount(String id) {
        String key = VideoManagerConstants.SEND_RTP_INFO_CALLID;
        return redisTemplate.opsForHash().size(key).intValue();
    }

    @Override
    public void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online) {
        String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_DEVICE_STATUS;
        StringBuilder msg = new StringBuilder();
        msg.append(deviceId);
        if (channelId != null) {
            msg.append(":").append(channelId);
        }
        msg.append(" ").append(online? "ON":"OFF");
        log.info("[redis通知] 推送设备/通道状态-> {} ", msg);
        // 使用 RedisTemplate<Object, Object> 发送字符串消息会导致发送的消息多带了双引号
        stringRedisTemplate.convertAndSend(key, msg.toString());
    }

    @Override
    public void sendChannelAddOrDelete(String deviceId, String channelId, boolean add) {
        String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_DEVICE_STATUS;


        StringBuilder msg = new StringBuilder();
        msg.append(deviceId);
        if (channelId != null) {
            msg.append(":").append(channelId);
        }
        msg.append(" ").append(add? "ADD":"DELETE");
        log.info("[redis通知] 推送通道-> {}", msg);
        // 使用 RedisTemplate<Object, Object> 发送字符串消息会导致发送的消息多带了双引号
        stringRedisTemplate.convertAndSend(key, msg.toString());
    }

    @Override
    public void sendPlatformStartPlayMsg(SendRtpInfo sendRtpItem, DeviceChannel channel, Platform platform) {
        if (platform == null) {
            log.info("[redis发送通知] 失败， 平台信息为NULL");
            return;
        }
        if (sendRtpItem.getPlayType() != InviteStreamType.PUSH) {
            log.info("[redis发送通知] 取消， 流来源通道不是推流设备");
            return;
        }
        MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, sendRtpItem.getApp(), sendRtpItem.getStream(),
                channel.getDeviceId(), platform.getServerGBId(), platform.getName(), userSetting.getServerId(),
                sendRtpItem.getMediaServerId());
        messageForPushChannel.setPlatFormIndex(platform.getId());
        String key = VideoManagerConstants.VM_MSG_STREAM_START_PLAY_NOTIFY;
        log.info("[redis发送通知] 发送 推流被上级平台观看 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), platform.getServerGBId());
        redisTemplate.convertAndSend(key, JSON.toJSON(messageForPushChannel));
    }

    @Override
    public void sendPlatformStopPlayMsg(SendRtpInfo sendRtpItem, Platform platform, CommonGBChannel channel) {

        MessageForPushChannel msg = MessageForPushChannel.getInstance(0,
                sendRtpItem.getApp(), sendRtpItem.getStream(), channel.getGbDeviceId(),
                sendRtpItem.getTargetId(), platform.getName(), userSetting.getServerId(), sendRtpItem.getMediaServerId());
        msg.setPlatFormIndex(platform.getId());

        String key = VideoManagerConstants.VM_MSG_STREAM_STOP_PLAY_NOTIFY;
        log.info("[redis发送通知] 发送 上级平台停止观看 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), platform.getServerGBId());
        redisTemplate.convertAndSend(key, JSON.toJSON(msg));
    }

    @Override
    public void addPushListItem(String app, String stream, MediaInfo mediaInfo) {
        String key = VideoManagerConstants.PUSH_STREAM_LIST + app + "_" + stream;
        redisTemplate.opsForValue().set(key, mediaInfo);
    }

    @Override
    public MediaInfo getPushListItem(String app, String stream) {
        String key = VideoManagerConstants.PUSH_STREAM_LIST + app + "_" + stream;
        return (MediaInfo)redisTemplate.opsForValue().get(key);
    }

    @Override
    public void removePushListItem(String app, String stream, String mediaServerId) {
        String key = VideoManagerConstants.PUSH_STREAM_LIST + app + "_" + stream;
        MediaInfo param = (MediaInfo)redisTemplate.opsForValue().get(key);
        if (param != null && userSetting.getServerId().equals(param.getServerId())) {
            redisTemplate.delete(key);
        }
    }

    @Override
    public void sendPushStreamClose(MessageForPushChannel msg) {
        String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE_REQUESTED;
        log.info("[redis发送通知] 发送 停止向上级推流 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId());
        redisTemplate.convertAndSend(key, JSON.toJSON(msg));
    }

    @Override
    public void addWaiteSendRtpItem(SendRtpInfo sendRtpItem, int platformPlayTimeout) {
        String key = VideoManagerConstants.WAITE_SEND_PUSH_STREAM + sendRtpItem.getApp() + "_" + sendRtpItem.getStream();
        redisTemplate.opsForValue().set(key, sendRtpItem);
    }

    @Override
    public SendRtpInfo getWaiteSendRtpItem(String app, String stream) {
        String key = VideoManagerConstants.WAITE_SEND_PUSH_STREAM + app + "_" + stream;
        return JsonUtil.redisJsonToObject(redisTemplate, key, SendRtpInfo.class);
    }

    @Override
    public void sendStartSendRtp(SendRtpInfo sendRtpItem) {
        String key = VideoManagerConstants.START_SEND_PUSH_STREAM + sendRtpItem.getApp() + "_" + sendRtpItem.getStream();
        log.info("[redis发送通知] 通知其他WVP推流 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getTargetId());
        redisTemplate.convertAndSend(key, JSON.toJSON(sendRtpItem));
    }

    @Override
    public void sendPushStreamOnline(SendRtpInfo sendRtpItem) {
        String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE_REQUESTED;
        log.info("[redis发送通知] 流上线 {}: {}/{}->{}", key, sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getTargetId());
        redisTemplate.convertAndSend(key, JSON.toJSON(sendRtpItem));
    }

    @Override
    public ServerInfo queryServerInfo(String serverId) {
        String key = VideoManagerConstants.WVP_SERVER_PREFIX + serverId;
        return (ServerInfo)redisTemplate.opsForValue().get(key);
    }

    @Override
    public String chooseOneServer(String serverId) {
        String key = VideoManagerConstants.WVP_SERVER_LIST;
        if (serverId != null) {
            redisTemplate.opsForZSet().remove(key, serverId);
        }
        // 获取得分最高的，也是最后更新时间到redis的wvp，这样可以避免读取到离线的wvp，同时时间最新也一定程度代表最健康的
        Set<Object> range = redisTemplate.opsForZSet().reverseRange(key, 0, 0);
        if (range == null || range.isEmpty()) {
            return null;
        }
        return (String) range.iterator().next();
    }

    @Override
    public void updateDeviceKeepaliveTimeStamp(List<Device> deviceList) {
        if (deviceList == null || deviceList.isEmpty()) {
            return;
        }
        // 使用 SessionCallback 保证批量操作在同一个连接中执行
        SessionCallback<Boolean> sessionCallback = new SessionCallback<>() {
            @Override
            // 注意：这里直接写死 String, String 覆盖接口的 K, V
            public Boolean execute(@NonNull RedisOperations operations) {
                // 1. 批量添加心跳数据到列表尾部
                for (Device device : deviceList) {
                    Duration duration = Duration.ofHours(1);
                    String key = VideoManagerConstants.DEVICE_KEEPALIVE_PREFIX + device.getDeviceId();
                    operations.opsForList().rightPush(key, device.getKeepaliveTimeStamp());
                    // 2. 截取列表，只保留最新 100 条
                    operations.opsForList().trim(key, -100, -1);

                    // 为整个列表 Key 设置过期时间（核心：覆盖式设置，每次更新心跳都重置过期时间）
                    operations.expire(key, duration);
                }
                return true;
            }
        };

        longRedisTemplate.execute(sessionCallback);
    }

    @Override
    public List<Long> getDeviceKeepaliveTimeStamp(String deviceId, Integer count) {
        if (deviceId == null ) {
            return List.of();
        }
        if (count == null) {
            count = 20;
        }
        return longRedisTemplate.opsForList().range(VideoManagerConstants.DEVICE_KEEPALIVE_PREFIX + deviceId, -count - 1, -1);
    }



    @Override
    public void updateDeviceRegisterTimeStamp(List<Device> deviceList) {
        if (deviceList == null || deviceList.isEmpty()) {
            return;
        }
        // 使用 SessionCallback 保证批量操作在同一个连接中执行
        SessionCallback<Boolean> sessionCallback = new SessionCallback<>() {
            @Override
            // 注意：这里直接写死 String, String 覆盖接口的 K, V
            public Boolean execute(@NonNull RedisOperations operations) {
                // 1. 批量添加心跳数据到列表尾部
                for (Device device : deviceList) {
                    Duration duration = Duration.ofHours(3);
                    String key = VideoManagerConstants.DEVICE_REGISTER_PREFIX + device.getDeviceId();
                    operations.opsForList().rightPush(key, device.getRegisterTimeStamp());
                    // 2. 截取列表，只保留最新 100 条
                    operations.opsForList().trim(key, -100, -1);

                    // 为整个列表 Key 设置过期时间（核心：覆盖式设置，每次更新心跳都重置过期时间）
                    operations.expire(key, duration);

                }
                return true;
            }
        };
        longRedisTemplate.execute(sessionCallback);
    }

    @Override
    public List<Long> getDeviceRegisterTimeStamp(String deviceId, Integer count) {
        if (deviceId == null ) {
            return List.of();
        }
        if (count == null) {
            count = 20;
        }
        return longRedisTemplate.opsForList().range(VideoManagerConstants.DEVICE_REGISTER_PREFIX + deviceId, -count - 1, -1);
    }
}
