package com.yushu.web.websocket;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yushu.common.core.domain.model.LoginUser;
import com.yushu.common.utils.StringUtils;
import com.yushu.common.utils.spring.SpringUtils;
import com.yushu.framework.web.service.TokenService;
import com.yushu.system.service.ISysNotificationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 消息WebSocket服务器
 * 用于实时推送消息通知
 */
@ServerEndpoint(value = "/ws/message", configurator = MessageWebSocketConfigurator.class)
@Component
public class MessageWebSocketServer {
    
    private static final Logger log = LoggerFactory.getLogger(MessageWebSocketServer.class);
    
    static {
        log.info("[WebSocket] MessageWebSocketServer 类正在加载...");
    }
    
    // 存储用户连接信息
    private static final ConcurrentHashMap<Long, ClientConnection> connections = new ConcurrentHashMap<>();
    
    // 心跳检测定时器
    private static final ScheduledExecutorService heartbeatExecutor = Executors.newScheduledThreadPool(1);
    
    // JSON序列化工具
    private static final ObjectMapper objectMapper = new ObjectMapper();
    
    // 当前连接的用户信息
    private LoginUser loginUser;
    private Long userId;
    private Session session;
    private long lastHeartbeat;
    
    static {
        // 启动心跳检测任务，每60秒检查一次
        heartbeatExecutor.scheduleAtFixedRate(() -> {
            long now = System.currentTimeMillis();
            connections.entrySet().removeIf(entry -> {
                ClientConnection conn = entry.getValue();
                if (now - conn.getLastHeartbeat() > 90000) { // 90秒未心跳则断开
                    try {
                        log.info("[WebSocket] 心跳超时，断开用户连接: {}", entry.getKey());
                        conn.getSession().close();
                    } catch (IOException e) {
                        log.error("[WebSocket] 关闭超时连接失败", e);
                    }
                    return true;
                }
                return false;
            });
        }, 60, 60, TimeUnit.SECONDS);
    }
    
    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        this.session = session;
        this.lastHeartbeat = System.currentTimeMillis();
        
        try {
            // 从配置中获取用户信息
            this.loginUser = (LoginUser) config.getUserProperties().get("loginUser");
            if (loginUser == null) {
                log.error("[WebSocket] 用户信息为空，拒绝连接");
                session.close();
                return;
            }
            
            this.userId = loginUser.getUserId();
            
            // 注册连接
            ClientConnection clientConn = new ClientConnection(session, loginUser, System.currentTimeMillis());
            connections.put(userId, clientConn);
            
            log.info("[WebSocket] 用户连接成功: {} ({}), 当前在线人数: {}", 
                    loginUser.getUsername(), userId, connections.size());
            
            // 发送连接确认消息
            sendConnectionAck();
            
            // 异步加载并推送未读消息数
            loadAndSendUnreadCount();
            
        } catch (Exception e) {
            log.error("[WebSocket] 连接建立失败", e);
            try {
                session.close();
            } catch (IOException ex) {
                log.error("[WebSocket] 关闭连接失败", ex);
            }
        }
    }
    
    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        if (userId != null) {
            connections.remove(userId);
            log.info("[WebSocket] 用户断开连接: {} ({}), 当前在线人数: {}", 
                    loginUser != null ? loginUser.getUsername() : "unknown", userId, connections.size());
        }
    }
    
    /**
     * 收到客户端消息后调用的方法
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        try {
            if (StringUtils.isEmpty(message)) {
                return;
            }
            
            // 更新心跳时间
            this.lastHeartbeat = System.currentTimeMillis();
            ClientConnection conn = connections.get(userId);
            if (conn != null) {
                conn.setLastHeartbeat(this.lastHeartbeat);
            }
            
            // 处理心跳
            if ("ping".equals(message)) {
                sendMessage("pong");
                return;
            }
            
            // 解析JSON消息
            Map<String, Object> msgData = objectMapper.readValue(message, Map.class);
            String type = (String) msgData.get("type");
            
            log.debug("[WebSocket] 收到用户消息: {} ({}), 类型: {}", 
                    loginUser.getUsername(), userId, type);
            
            // 处理不同类型的消息
            switch (type) {
                case "pong":
                    // 心跳响应，无需处理
                    break;
                case "read_ack":
                    handleReadAck(msgData);
                    break;
                default:
                    log.warn("[WebSocket] 未知消息类型: {}", type);
            }
            
        } catch (Exception e) {
            log.error("[WebSocket] 处理消息失败: {}", message, e);
        }
    }
    
    /**
     * 发生错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("[WebSocket] 连接错误: {} ({})", 
                loginUser != null ? loginUser.getUsername() : "unknown", 
                userId, error);
    }
    
    /**
     * 发送连接确认消息
     */
    private void sendConnectionAck() {
        try {
            Map<String, Object> ackMessage = Map.of(
                "type", "connection_ack",
                "connectedAt", System.currentTimeMillis(),
                "userId", userId,
                "username", loginUser.getUsername()
            );
            sendMessage(objectMapper.writeValueAsString(ackMessage));
        } catch (Exception e) {
            log.error("[WebSocket] 发送连接确认失败", e);
        }
    }
    
    /**
     * 加载并发送未读消息数
     */
    private void loadAndSendUnreadCount() {
        try {
            ISysNotificationService notificationService = SpringUtils.getBean(ISysNotificationService.class);
            // 获取用户未读通知数
            int unreadCount = notificationService.countUnreadNotifications(userId);
            
            Map<String, Object> snapshot = Map.of(
                "type", "unread_snapshot",
                "payload", Map.of("unreadCount", unreadCount),
                "timestamp", System.currentTimeMillis()
            );
            
            sendMessage(objectMapper.writeValueAsString(snapshot));
            log.debug("[WebSocket] 发送未读数快照: {}", unreadCount);
            
        } catch (Exception e) {
            log.error("[WebSocket] 加载未读消息数失败", e);
        }
    }
    
    /**
     * 处理已读确认
     */
    private void handleReadAck(Map<String, Object> msgData) {
        try {
            Object payload = msgData.get("payload");
            if (payload instanceof Map) {
                Map<String, Object> payloadMap = (Map<String, Object>) payload;
                Object messageIds = payloadMap.get("messageIds");
                
                if (messageIds instanceof java.util.List) {
                    // 这里可以调用服务标记消息为已读
                    log.info("[WebSocket] 处理已读确认: 用户 {} 标记消息已读", userId);
                    // TODO: 实现批量标记已读逻辑
                }
            }
        } catch (Exception e) {
            log.error("[WebSocket] 处理已读确认失败", e);
        }
    }
    
    /**
     * 发送消息给当前连接
     */
    private void sendMessage(String message) throws IOException {
        if (session != null && session.isOpen()) {
            synchronized (session) {
                session.getBasicRemote().sendText(message);
            }
        }
    }
    
    /**
     * 发送消息给指定用户
     */
    public static void sendToUser(Long userId, Object message) {
        ClientConnection conn = connections.get(userId);
        if (conn != null && conn.getSession().isOpen()) {
            try {
                String jsonMessage = objectMapper.writeValueAsString(message);
                synchronized (conn.getSession()) {
                    conn.getSession().getBasicRemote().sendText(jsonMessage);
                }
                log.debug("[WebSocket] 发送消息给用户: {}", userId);
            } catch (Exception e) {
                log.error("[WebSocket] 发送消息失败: 用户 {}", userId, e);
            }
        } else {
            log.debug("[WebSocket] 用户不在线: {}", userId);
        }
    }
    
    /**
     * 广播消息给多个用户
     */
    public static void sendToUsers(java.util.Collection<Long> userIds, Object message) {
        if (userIds == null || userIds.isEmpty()) {
            return;
        }
        
        log.info("[WebSocket] 准备发送消息给用户: {}, 当前在线用户: {}", userIds, connections.keySet());
        
        try {
            String jsonMessage = objectMapper.writeValueAsString(message);
            int sentCount = 0;
            for (Long userId : userIds) {
                ClientConnection conn = connections.get(userId);
                if (conn != null && conn.getSession().isOpen()) {
                    try {
                        synchronized (conn.getSession()) {
                            conn.getSession().getBasicRemote().sendText(jsonMessage);
                        }
                        sentCount++;
                        log.info("[WebSocket] 成功发送消息给用户: {}", userId);
                    } catch (Exception e) {
                        log.error("[WebSocket] 发送消息失败: 用户 {}", userId, e);
                    }
                } else {
                    log.info("[WebSocket] 用户不在线，跳过: {}", userId);
                }
            }
            log.info("[WebSocket] 实际发送给 {} 个用户（请求 {} 个）", sentCount, userIds.size());
        } catch (Exception e) {
            log.error("[WebSocket] 广播消息失败", e);
        }
    }
    
    /**
     * 获取在线用户数
     */
    public static int getOnlineCount() {
        return connections.size();
    }
    
    /**
     * 获取在线用户列表
     */
    public static java.util.Set<Long> getOnlineUsers() {
        return connections.keySet();
    }
    
    /**
     * 客户端连接信息
     */
    private static class ClientConnection {
        private final Session session;
        private final LoginUser loginUser;
        private volatile long lastHeartbeat;
        
        public ClientConnection(Session session, LoginUser loginUser, long lastHeartbeat) {
            this.session = session;
            this.loginUser = loginUser;
            this.lastHeartbeat = lastHeartbeat;
        }
        
        public Session getSession() {
            return session;
        }
        
        public LoginUser getLoginUser() {
            return loginUser;
        }
        
        public long getLastHeartbeat() {
            return lastHeartbeat;
        }
        
        public void setLastHeartbeat(long lastHeartbeat) {
            this.lastHeartbeat = lastHeartbeat;
        }
    }
}
