package com.ruoyi.device.websocket;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.device.websocket.model.TsbWebSocketMessage;
import com.ruoyi.framework.websocket.SemaphoreUtils;
import com.ruoyi.framework.websocket.WebSocketUtils;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.Semaphore;

/**
 * 调试宝 Web 端 WebSocket：/websocket/tsb?token=xxx 发送鉴权登录令牌
 * <p>
 * 连接标识：deviceSn
 *
 * @author lwm
 */
@Component
@ServerEndpoint(value = "/websocket/tsb", configurator = TsbSpringWebSocketConfigurator.class)
public class TsbWebSocketServer
{
    /**
     * TsbWebSocketServer 日志控制器
     */
    private static final Logger log = LoggerFactory.getLogger(TsbWebSocketServer.class);

    /**
     * 信号量，用于控制同时在线人数
     */
    private static Semaphore socketSemaphore = new Semaphore(TsbWebSocketConstants.SOCKET_MAX_ONLINE_COUNT);

    private final TsbWebSocketService tsbWebSocketService;

    public TsbWebSocketServer(TsbWebSocketService tsbWebSocketService)
    {
        this.tsbWebSocketService = tsbWebSocketService;
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session) throws IOException {
        boolean semaphoreFlag = false;
        // 尝试获取信号量
        semaphoreFlag = SemaphoreUtils.tryAcquire(socketSemaphore);
        if (!semaphoreFlag)
        {
            // 未获取到信号量
            log.error("\n 当前在线人数超过限制数- {}", TsbWebSocketConstants.SOCKET_MAX_ONLINE_COUNT);
            TsbWebSocketUsers.sendMessageToUserByText(session,
                    TsbWebSocketMessage.fail(TsbWebSocketConstants.CONNECT, "当前在线人数超过限制数：" + TsbWebSocketConstants.SOCKET_MAX_ONLINE_COUNT));
            WebSocketUtils.closeQuietly(session, CloseReason.CloseCodes.TRY_AGAIN_LATER, "超过连接数限制");
            return;
        }
        // 用户鉴权
        String token = WebSocketUtils.resolveQueryParam(session, TsbWebSocketConstants.TOKEN_PARAM_NAME);
        String deviceSnText = WebSocketUtils.resolveQueryParam(session, TsbWebSocketConstants.DEVICE_SN_PARAM_NAME);
        Long deviceSn = parseDeviceSn(deviceSnText);
        StringBuilder failReason = new StringBuilder();
        JSONObject bind = tsbWebSocketService.authenticate(token, deviceSn, session, failReason);
        if (bind == null)
        {
            String reason = !failReason.isEmpty() ? failReason.toString() : "鉴权失败或未绑定设备";
            log.error("WebSocket 连接失败：{}", reason);
            TsbWebSocketUsers.sendMessageToUserByText(session,
                    TsbWebSocketMessage.fail(TsbWebSocketConstants.CONNECT, reason));
            WebSocketUtils.closeQuietly(session, CloseReason.CloseCodes.CANNOT_ACCEPT, reason);
            SemaphoreUtils.release(socketSemaphore);
            return;
        }
        log.info("当前连接人数:{}", TsbWebSocketUsers.getSessionUsers().size());
        TsbWebSocketUsers.sendMessageToUserByText(session,
                TsbWebSocketMessage.success(TsbWebSocketConstants.CONNECT, "连接成功", bind));
    }

    /**
     * 连接关闭时处理
     */
    @OnClose
    public void onClose(Session session)
    {
        Long deviceSn = tsbWebSocketService.getDeviceSnFromSession(session);
        if (deviceSn != null)
        {
            log.info("WebSocket 连接关闭, deviceSn={}", deviceSn);
            // 移除用户
            boolean removeFlag = TsbWebSocketUsers.remove(deviceSn);
            if (!removeFlag)
            {
                // 获取到信号量则需释放
                SemaphoreUtils.release(socketSemaphore);
            }
        }
        else
        {
            log.info("WebSocket 连接关闭");
        }
    }

    /**
     * 抛出异常时处理
     */
    @OnError
    public void onError(Session session, Throwable error) throws Exception
    {
        log.info("WebSocket 异常, sessionId={}, err={}", session.getId(), error.getMessage());
        if (session.isOpen())
        {
            // 关闭连接
            session.close();
        }
        Long deviceSn = tsbWebSocketService.getDeviceSnFromSession(session);
        if (deviceSn != null)
        {
            log.info("WebSocket 连接异常, deviceSn={}", deviceSn);
            // 移除用户
            TsbWebSocketUsers.remove(deviceSn);
        }
        else
        {
            log.info("WebSocket 连接异常");
        }
        // 获取到信号量则需释放
        SemaphoreUtils.release(socketSemaphore);
    }

    /**
     * 服务器接收到客户端消息时调用的方法
     */
    @OnMessage
    public void onMessage(String message, Session session)
    {
        tsbWebSocketService.pushDeviceSyncFromPage(session, message);
    }

    private Long parseDeviceSn(String deviceSnText)
    {
        if (deviceSnText == null || deviceSnText.isEmpty())
        {
            return null;
        }
        try
        {
            return Long.valueOf(deviceSnText);
        }
        catch (NumberFormatException e)
        {
            return null;
        }
    }
}
