package com.ssyf.task.websocket;

import cn.hutool.core.date.DateUtil;
import com.ssyf.common.core.func.Callback;
import com.ssyf.common.core.func.CallbackP;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * WS 统计监控服务
 * 负责大屏数据的实时计算与存储
 */
@Slf4j
@Service
public class WsStatisticsService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private static final String KEY_GLOBAL_COUNT = "ws:stats:global:count";
    private static final String KEY_GLOBAL_DISCONNECT_TOTAL = "ws:stats:global:disconnect_total";
    private static final String KEY_ACTIVE_ROOMS_SET = "ws:stats:active:rooms";
    private static final String KEY_ROOM_STATS_PREFIX = "ws:stats:room:";

    private Map<Integer , List<CallbackP<String>>> roomIdCallbacks = new ConcurrentHashMap<>();

    /**
     * 记录连接建立
     */
    public void recordConnect(Integer roomId, String roomName, String anchorName) {
        String roomStatsKey = KEY_ROOM_STATS_PREFIX + roomId;
        long now = System.currentTimeMillis();

        // 1. 全局计数 +1
        redisTemplate.opsForValue().increment(KEY_GLOBAL_COUNT);
        
        // 2. 加入活跃房间集合
        redisTemplate.opsForSet().add(KEY_ACTIVE_ROOMS_SET, roomId);

        // 3. 更新单房间详情 (Hash)
        Map<String, Object> updates = new HashMap<>();
        updates.put("roomId", roomId);
        updates.put("roomName", roomName);
        updates.put("anchorName", anchorName);
        updates.put("connectTime", now);
        updates.put("connectTimeStr", DateUtil.date(now).toString());
        updates.put("status", "CONNECTED");
        // 如果是首次连接，初始化断链重连次数
        redisTemplate.opsForHash().putIfAbsent(roomStatsKey, "disconnectCount", 0);
        redisTemplate.opsForHash().putIfAbsent(roomStatsKey, "reconnectCount", 0);
        
        redisTemplate.opsForHash().putAll(roomStatsKey, updates);
        // 设置过期时间，防止死数据堆积 (例如7天，保证短期内可查历史)
        redisTemplate.expire(roomStatsKey, 7, TimeUnit.DAYS);
    }

    /**
     * 记录连接断开
     */
    public void recordDisconnect(Integer roomId) {
        String roomStatsKey = KEY_ROOM_STATS_PREFIX + roomId;
        long now = System.currentTimeMillis();

        // 1. 全局计数 -1
        redisTemplate.opsForValue().decrement(KEY_GLOBAL_COUNT);
        
        // 2. 从活跃集合移除
        redisTemplate.opsForSet().remove(KEY_ACTIVE_ROOMS_SET, roomId);
        
        // 3. 全局累计断链数 +1
        redisTemplate.opsForValue().increment(KEY_GLOBAL_DISCONNECT_TOTAL);

        // 4. 更新单房间统计
        if (Boolean.TRUE.equals(redisTemplate.hasKey(roomStatsKey))) {
            redisTemplate.opsForHash().increment(roomStatsKey, "disconnectCount", 1);
            redisTemplate.opsForHash().put(roomStatsKey, "exitTime", now);
            redisTemplate.opsForHash().put(roomStatsKey, "exitTimeStr", DateUtil.date(now).toString());
            redisTemplate.opsForHash().put(roomStatsKey, "status", "DISCONNECTED");
        }
    }

    public synchronized void updateClientId(Integer roomId,String clientId){
        String roomStatsKey = KEY_ROOM_STATS_PREFIX + roomId;
        redisTemplate.opsForHash().put(roomStatsKey, "clientId", clientId);

        roomIdCallbacks.computeIfPresent(roomId, (k, v) -> {
            for (CallbackP<String> callback : v) {
                callback.call(clientId);
            }
            return null;
        });
        roomIdCallbacks.remove(roomId);

        //通知其他的服务
        WsMessageTask task = new WsMessageTask();
        task.setType("updateClientId").setRoomId(roomId);
        //其他设备在线,redis广播通知
        redisTemplate.convertAndSend(WsMessageListener.TOPIC_PREFIX, task);
    }


    public synchronized void updateClientIdByListener(Integer roomId) {
        String roomStatsKey = KEY_ROOM_STATS_PREFIX + roomId;
        Object object = redisTemplate.opsForHash().get(roomStatsKey, "clientId");
        if(object == null){
            return;
        }
        //获取clientId
        String clientId =object.toString();


        roomIdCallbacks.computeIfPresent(roomId, (k, v) -> {
            for (CallbackP<String> callback : v) {
                callback.call(clientId);
            }
            return null;
        });
        roomIdCallbacks.remove(roomId);
    }

    /**
     * 记录重连尝试
     */
    public void recordReconnectAttempt(Integer roomId) {
        String roomStatsKey = KEY_ROOM_STATS_PREFIX + roomId;
        redisTemplate.opsForHash().increment(roomStatsKey, "reconnectCount", 1);
    }

    /**
     * 获取大屏核心数据
     */
    public WsBigScreenData getBigScreenOverview() {
        Object totalConnObj = redisTemplate.opsForValue().get(KEY_GLOBAL_COUNT);
        Object totalDiscObj = redisTemplate.opsForValue().get(KEY_GLOBAL_DISCONNECT_TOTAL);
        Long activeRoomCount = redisTemplate.opsForSet().size(KEY_ACTIVE_ROOMS_SET);

        int totalConn = totalConnObj != null ? Integer.parseInt(totalConnObj.toString()) : 0;
        int totalDisc = totalDiscObj != null ? Integer.parseInt(totalDiscObj.toString()) : 0;
        
        // 修正负数情况（由于Redis重启或手动操作可能导致不同步）
        if (totalConn < 0) {
            totalConn = 0;
        }

        return WsBigScreenData.builder()
                .totalConnections(totalConn)
                .totalDisconnects(totalDisc)
                .activeRoomCount(activeRoomCount != null ? activeRoomCount.intValue() : 0)
                .serverUptime(System.currentTimeMillis()) // 实际上应该取应用启动时间，这里简化
                .build();
    }

    public Map<Object, Object> getByRoomId(Integer roomId) {
        String roomStatsKey = KEY_ROOM_STATS_PREFIX + roomId;
        return redisTemplate.opsForHash().entries(roomStatsKey);
    }

    public synchronized void registerCallback(Integer userId, CallbackP<String> callback) {
        roomIdCallbacks.computeIfAbsent(userId, k -> new java.util.ArrayList<>()).add(callback);
    }

}