package cn.novalon.manage.notify.websocket;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.web.reactive.socket.HandshakeInfo;
import org.springframework.web.reactive.socket.WebSocketMessage;
import org.springframework.web.reactive.socket.WebSocketSession;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import java.net.URI;

import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class SysWebSocketHandlerTest {

    @Mock
    private WebSocketSession session;

    @Mock
    private WebSocketMessage message;

    @Mock
    private HandshakeInfo handshakeInfo;

    private SysWebSocketHandler webSocketHandler;

    @BeforeEach
    void setUp() {
        webSocketHandler = new SysWebSocketHandler();
    }

    @Test
    void testHandle_NewConnection() {
        when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
        when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=testuser"));
        when(session.receive()).thenReturn(Flux.empty());
        
        Mono<Void> result = webSocketHandler.handle(session);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        verify(session).receive();
    }

    @Test
    void testHandle_WithUserId() {
        when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
        when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=123"));
        when(session.receive()).thenReturn(Flux.empty());
        
        Mono<Void> result = webSocketHandler.handle(session);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        verify(session).receive();
    }

    @Test
    void testHandle_WithoutUserId() {
        when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
        when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws"));
        when(session.getId()).thenReturn("test-session-id");
        when(session.receive()).thenReturn(Flux.empty());
        
        Mono<Void> result = webSocketHandler.handle(session);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        verify(session).receive();
    }

    @Test
    void testHandle_PongMessage() {
        when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
        when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=testuser"));
        when(message.getPayloadAsText()).thenReturn("{\"type\":\"pong\"}");
        when(session.receive()).thenReturn(Flux.just(message));
        
        Mono<Void> result = webSocketHandler.handle(session);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        verify(session).receive();
        verify(message).getPayloadAsText();
    }

    @Test
    void testHandle_SubscribeMessage() {
        when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
        when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=testuser"));
        when(message.getPayloadAsText()).thenReturn("{\"type\":\"subscribe\"}");
        when(session.receive()).thenReturn(Flux.just(message));
        
        Mono<Void> result = webSocketHandler.handle(session);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        verify(session).receive();
        verify(message).getPayloadAsText();
    }

    @Test
    void testHandle_HeartbeatMessage() {
        when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
        when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=testuser"));
        when(message.getPayloadAsText()).thenReturn("{\"type\":\"heartbeat\"}");
        when(session.receive()).thenReturn(Flux.just(message));
        
        Mono<Void> result = webSocketHandler.handle(session);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        verify(session).receive();
        verify(message).getPayloadAsText();
    }

    @Test
    void testHandle_UnknownMessageType() {
        when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
        when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=testuser"));
        when(message.getPayloadAsText()).thenReturn("{\"type\":\"unknown\"}");
        when(session.receive()).thenReturn(Flux.just(message));
        
        Mono<Void> result = webSocketHandler.handle(session);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        verify(session).receive();
        verify(message).getPayloadAsText();
    }

    @Test
    void testHandle_InvalidJson() {
        when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
        when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=testuser"));
        when(message.getPayloadAsText()).thenReturn("invalid json");
        when(session.receive()).thenReturn(Flux.just(message));
        
        Mono<Void> result = webSocketHandler.handle(session);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        verify(session).receive();
        verify(message).getPayloadAsText();
    }

    @Test
    void testHandle_SessionError() {
        when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
        when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=testuser"));
        when(session.receive()).thenReturn(Flux.error(new RuntimeException("Connection error")));
        
        Mono<Void> result = webSocketHandler.handle(session);
        
        StepVerifier.create(result)
                .verifyError();
        
        verify(session).receive();
    }

    @Test
    void testSendMessageToUser_SessionNotFound() {
        webSocketHandler.sendMessageToUser("nonexistent", java.util.Map.of("type", "notification", "message", "test"));
        
        verify(session, never()).send(any());
    }
}
