package com.huashe.park.common.websocket;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.PongMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator;

import com.alibaba.fastjson2.JSONObject;

public abstract class SocketHandle extends AbstractWebSocketHandler implements ISocketHandle {
    private static final Logger log = LoggerFactory.getLogger(SocketHandle.class);

    private final Map<String, WebSocketSession> webSocketSessionMap = new ConcurrentHashMap<>();

    // 成功连接时
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        String sessionKey = custSessionKey(session);
        webSocketSessionMap.put(sessionKey, new ConcurrentWebSocketSessionDecorator(session, 10 * 1000, 64000));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        super.handleTextMessage(session, message);
    }

    @Override
    public void sendMessage(String sessionKey, JSONObject message) {
        List<WebSocketSession> filterSessions = filterSession(sessionKey, webSocketSessionMap);
        if (CollectionUtils.isEmpty(filterSessions)) {
            return;
        }
        filterSessions.forEach(webSocketSession -> {
            try {
                webSocketSession.sendMessage(new TextMessage(message.toJSONString()));
            }
            catch (Exception e) {
                log.error("sendMessage error:{}", e.getMessage(), e);
            }
        });
    }

    public abstract List<WebSocketSession> filterSession(String sessionKey,
        Map<String, WebSocketSession> webSocketSessionMap);

    // 报错时
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        super.handleTransportError(session, exception);
        log.warn("handleTransportError:: sessionId: {} {}", session.getId(), exception.getMessage(), exception);
        if (session.isOpen()) {
            try {
                session.close();
            }
            catch (Exception ex) {
                // ignore
            }
        }
    }

    // 连接关闭时
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        String sessionKey = custSessionKey(session);
        super.afterConnectionClosed(session, status);
        webSocketSessionMap.remove(sessionKey);
        if (session.isOpen()) {
            try {
                session.close();
            }
            catch (Exception ex) {
                // ignore
            }
        }
    }

    // 处理binarymessage
    @Override
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
        super.handleBinaryMessage(session, message);
    }

    // 处理pong
    @Override
    protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
        super.handlePongMessage(session, message);
    }

    // 是否支持报文拆包
    // 决定是否接受半包，因为websocket协议比较底层，好像Tcp协议一样，如果发送大消息可能会拆成多个小报文。如果不希望处理不完整的报文，希望底层帮忙聚合成完整消息将此方法返回false,这样底层会等待完整报文到达聚合后才回调。
    @Override
    public boolean supportsPartialMessages() {
        return super.supportsPartialMessages();
    }
}
