package com.cmvr.web.controller.api;

import com.cmvr.edge.client.service.AudioService;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;

/**
 * WebRTC 信令 WebSocket 入口。
 *
 * 路径：/ws/signaling
 *
 * 约束：
 * 1. WebSocket 只传 JSON 信令：join、offer、answer、candidate、heartbeat、stop；
 * 2. WebSocket 禁止传输 PCM 音频；
 * 3. PCM 音频由 WebRTC 媒体通道进入 Java 后端，再由 AudioService 转 gRPC 双向流。
 */
@Configuration
@RequiredArgsConstructor
public class WebRtcSignalingController implements WebSocketConfigurer {

    private final AudioService audioService;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new SignalingWebSocketHandler(audioService), "/ws/signaling")
                .setAllowedOrigins("*");
        registry.addHandler(new SignalingWebSocketHandler(audioService), "/ws/audio")
                .setAllowedOrigins("*");
    }

    /**
     * 信令处理器。这里只处理文本 JSON，不处理二进制音频。
     */
    private static class SignalingWebSocketHandler extends AbstractWebSocketHandler {

        private static final Logger log = LoggerFactory.getLogger(SignalingWebSocketHandler.class);

        private final AudioService audioService;
        private final ObjectMapper objectMapper = new ObjectMapper();

        private SignalingWebSocketHandler(AudioService audioService) {
            this.audioService = audioService;
        }

        @Override
        public void afterConnectionEstablished(@NotNull WebSocketSession session) {
            log.info("WebRTC信令连接建立，wsSessionId={}", session.getId());
            sendConnected(session);
        }

        @Override
        protected void handleTextMessage(@NotNull WebSocketSession session, @NotNull TextMessage message) {
            try {
                audioService.handleSignalingMessage(session, message.getPayload());
            } catch (Exception e) {
                log.error("处理WebRTC信令异常，wsSessionId={}", session.getId(), e);
                sendError(session, "处理WebRTC信令异常：" + e.getMessage());
            }
        }

        @Override
        protected void handleBinaryMessage(@NotNull WebSocketSession session, @NotNull BinaryMessage message) {
            try {
                audioService.handleBrowserAudioMessage(session, message.getPayload());
            } catch (Exception e) {
                log.error("处理WebSocket音频二进制帧失败，wsSessionId={}", session.getId(), e);
                sendError(session, "audio binary frame failed: " + e.getMessage());
            }
        }

        @Override
        public void handleTransportError(@NotNull WebSocketSession session, @NotNull Throwable exception) {
            log.error("WebRTC信令连接传输异常，wsSessionId={}", session.getId(), exception);
            audioService.handleWebSocketClosed(session);
        }

        @Override
        public void afterConnectionClosed(@NotNull WebSocketSession session, @NotNull CloseStatus status) {
            log.info("WebRTC信令连接关闭，wsSessionId={}，status={}", session.getId(), status);
            audioService.handleWebSocketClosed(session);
        }

        private void sendConnected(WebSocketSession session) {
            ObjectNode node = objectMapper.createObjectNode();
            node.put("type", "connected");
            node.put("timestamp", System.currentTimeMillis());
            sendJson(session, node);
        }

        private void sendError(WebSocketSession session, String message) {
            ObjectNode node = objectMapper.createObjectNode();
            node.put("type", "error");
            node.put("message", message);
            node.put("timestamp", System.currentTimeMillis());
            sendJson(session, node);
        }

        private void sendJson(WebSocketSession session, ObjectNode node) {
            if (session == null || !session.isOpen()) {
                return;
            }
            try {
                synchronized (session) {
                    session.sendMessage(new TextMessage(objectMapper.writeValueAsString(node)));
                }
            } catch (Exception e) {
                log.error("发送WebRTC信令响应失败，wsSessionId={}", session.getId(), e);
            }
        }
    }
}
