package cn.novalon.manage.sys.handler.log;

import cn.novalon.manage.sys.core.domain.SysLoginLog;
import cn.novalon.manage.sys.core.domain.SysExceptionLog;
import cn.novalon.manage.sys.core.service.ISysLoginLogService;
import cn.novalon.manage.sys.core.service.ISysExceptionLogService;
import cn.novalon.manage.common.dto.PageResponse;
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.http.HttpStatus;
import org.springframework.mock.web.reactive.function.server.MockServerRequest;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import java.time.LocalDateTime;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class SysLogHandlerTest {

    @Mock
    private ISysLoginLogService loginLogService;

    @Mock
    private ISysExceptionLogService exceptionLogService;

    private SysLogHandler logHandler;
    private SysLoginLog testLoginLog;
    private SysExceptionLog testExceptionLog;

    @BeforeEach
    void setUp() {
        logHandler = new SysLogHandler(loginLogService, exceptionLogService);
        
        testLoginLog = new SysLoginLog();
        testLoginLog.setId(1L);
        testLoginLog.setUsername("testuser");
        testLoginLog.setIp("192.168.1.1");
        testLoginLog.setStatus("1");
        testLoginLog.setLoginTime(LocalDateTime.now());
        
        testExceptionLog = new SysExceptionLog();
        testExceptionLog.setId(1L);
        testExceptionLog.setUsername("testuser");
        testExceptionLog.setTitle("test operation");
        testExceptionLog.setExceptionName("NullPointerException");
        testExceptionLog.setExceptionMsg("Test exception");
        testExceptionLog.setCreateTime(LocalDateTime.now());
    }

    @Test
    void testGetAllLoginLogs() {
        when(loginLogService.findAll()).thenReturn(Flux.just(testLoginLog));
        
        ServerRequest request = MockServerRequest.builder().build();
        Mono<ServerResponse> response = logHandler.getAllLoginLogs(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(loginLogService).findAll();
    }

    @Test
    void testGetAllLoginLogs_WithPagination() {
        PageResponse<SysLoginLog> pageResponse = new PageResponse<>();
        pageResponse.setContent(java.util.Collections.singletonList(testLoginLog));
        pageResponse.setTotalElements(1L);
        pageResponse.setTotalPages(1);
        
        when(loginLogService.findLoginLogsByPage(any())).thenReturn(Mono.just(pageResponse));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("page", "0")
                .queryParam("size", "10")
                .build();
        Mono<ServerResponse> response = logHandler.getAllLoginLogs(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(loginLogService).findLoginLogsByPage(any());
    }

    @Test
    void testGetAllLoginLogs_WithOnlyPageParam() {
        PageResponse<SysLoginLog> pageResponse = new PageResponse<>();
        pageResponse.setContent(java.util.Collections.singletonList(testLoginLog));
        pageResponse.setTotalElements(1L);
        
        when(loginLogService.findLoginLogsByPage(any())).thenReturn(Mono.just(pageResponse));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("page", "0")
                .build();
        Mono<ServerResponse> response = logHandler.getAllLoginLogs(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(loginLogService).findLoginLogsByPage(any());
    }

    @Test
    void testGetLoginLogById() {
        when(loginLogService.findById(1L)).thenReturn(Mono.just(testLoginLog));
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "1")
                .build();
        Mono<ServerResponse> response = logHandler.getLoginLogById(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(loginLogService).findById(1L);
    }

    @Test
    void testGetLoginLogById_NotFound() {
        when(loginLogService.findById(999L)).thenReturn(Mono.empty());
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "999")
                .build();
        Mono<ServerResponse> response = logHandler.getLoginLogById(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.NOT_FOUND)
                .verifyComplete();
        
        verify(loginLogService).findById(999L);
    }

    @Test
    void testCreateLoginLog() {
        SysLoginLog newLoginLog = new SysLoginLog();
        newLoginLog.setUsername("newuser");
        newLoginLog.setIp("192.168.1.2");
        newLoginLog.setStatus("1");
        
        when(loginLogService.save(any())).thenReturn(Mono.just(testLoginLog));
        
        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(newLoginLog));
        Mono<ServerResponse> response = logHandler.createLoginLog(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.CREATED)
                .verifyComplete();
        
        verify(loginLogService).save(any());
    }

    @Test
    void testGetLoginLogsByPage() {
        PageResponse<SysLoginLog> pageResponse = new PageResponse<>();
        pageResponse.setContent(java.util.Collections.singletonList(testLoginLog));
        pageResponse.setTotalElements(1L);
        pageResponse.setTotalPages(1);
        
        when(loginLogService.findLoginLogsByPage(any())).thenReturn(Mono.just(pageResponse));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("page", "0")
                .queryParam("size", "10")
                .build();
        Mono<ServerResponse> response = logHandler.getLoginLogsByPage(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(loginLogService).findLoginLogsByPage(any());
    }

    @Test
    void testGetLoginLogsByPage_WithKeyword() {
        PageResponse<SysLoginLog> pageResponse = new PageResponse<>();
        pageResponse.setContent(java.util.Collections.singletonList(testLoginLog));
        pageResponse.setTotalElements(1L);
        
        when(loginLogService.findLoginLogsByPage(any())).thenReturn(Mono.just(pageResponse));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("page", "0")
                .queryParam("size", "10")
                .queryParam("keyword", "test")
                .build();
        Mono<ServerResponse> response = logHandler.getLoginLogsByPage(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(loginLogService).findLoginLogsByPage(any());
    }

    @Test
    void testGetLoginLogCount() {
        when(loginLogService.count()).thenReturn(Mono.just(100L));
        
        ServerRequest request = MockServerRequest.builder().build();
        Mono<ServerResponse> response = logHandler.getLoginLogCount(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(loginLogService).count();
    }

    @Test
    void testGetAllExceptionLogs() {
        when(exceptionLogService.findAll()).thenReturn(Flux.just(testExceptionLog));
        
        ServerRequest request = MockServerRequest.builder().build();
        Mono<ServerResponse> response = logHandler.getAllExceptionLogs(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(exceptionLogService).findAll();
    }

    @Test
    void testGetAllExceptionLogs_WithPagination() {
        PageResponse<SysExceptionLog> pageResponse = new PageResponse<>();
        pageResponse.setContent(java.util.Collections.singletonList(testExceptionLog));
        pageResponse.setTotalElements(1L);
        pageResponse.setTotalPages(1);
        
        when(exceptionLogService.findExceptionLogsByPage(any())).thenReturn(Mono.just(pageResponse));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("page", "0")
                .queryParam("size", "10")
                .build();
        Mono<ServerResponse> response = logHandler.getAllExceptionLogs(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(exceptionLogService).findExceptionLogsByPage(any());
    }

    @Test
    void testGetAllExceptionLogs_WithOnlySizeParam() {
        PageResponse<SysExceptionLog> pageResponse = new PageResponse<>();
        pageResponse.setContent(java.util.Collections.singletonList(testExceptionLog));
        pageResponse.setTotalElements(1L);
        
        when(exceptionLogService.findExceptionLogsByPage(any())).thenReturn(Mono.just(pageResponse));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("size", "10")
                .build();
        Mono<ServerResponse> response = logHandler.getAllExceptionLogs(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(exceptionLogService).findExceptionLogsByPage(any());
    }

    @Test
    void testGetExceptionLogById() {
        when(exceptionLogService.findById(1L)).thenReturn(Mono.just(testExceptionLog));
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "1")
                .build();
        Mono<ServerResponse> response = logHandler.getExceptionLogById(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(exceptionLogService).findById(1L);
    }

    @Test
    void testGetExceptionLogById_NotFound() {
        when(exceptionLogService.findById(999L)).thenReturn(Mono.empty());
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "999")
                .build();
        Mono<ServerResponse> response = logHandler.getExceptionLogById(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.NOT_FOUND)
                .verifyComplete();
        
        verify(exceptionLogService).findById(999L);
    }

    @Test
    void testCreateExceptionLog() {
        SysExceptionLog newExceptionLog = new SysExceptionLog();
        newExceptionLog.setUsername("newuser");
        newExceptionLog.setTitle("new operation");
        newExceptionLog.setExceptionName("RuntimeException");
        newExceptionLog.setExceptionMsg("New exception");
        
        when(exceptionLogService.save(any())).thenReturn(Mono.just(testExceptionLog));
        
        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(newExceptionLog));
        Mono<ServerResponse> response = logHandler.createExceptionLog(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.CREATED)
                .verifyComplete();
        
        verify(exceptionLogService).save(any());
    }

    @Test
    void testGetExceptionLogsByPage() {
        PageResponse<SysExceptionLog> pageResponse = new PageResponse<>();
        pageResponse.setContent(java.util.Collections.singletonList(testExceptionLog));
        pageResponse.setTotalElements(1L);
        pageResponse.setTotalPages(1);
        
        when(exceptionLogService.findExceptionLogsByPage(any())).thenReturn(Mono.just(pageResponse));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("page", "0")
                .queryParam("size", "10")
                .build();
        Mono<ServerResponse> response = logHandler.getExceptionLogsByPage(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(exceptionLogService).findExceptionLogsByPage(any());
    }

    @Test
    void testGetExceptionLogsByPage_WithKeyword() {
        PageResponse<SysExceptionLog> pageResponse = new PageResponse<>();
        pageResponse.setContent(java.util.Collections.singletonList(testExceptionLog));
        pageResponse.setTotalElements(1L);
        
        when(exceptionLogService.findExceptionLogsByPage(any())).thenReturn(Mono.just(pageResponse));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("page", "0")
                .queryParam("size", "10")
                .queryParam("keyword", "test")
                .build();
        Mono<ServerResponse> response = logHandler.getExceptionLogsByPage(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(exceptionLogService).findExceptionLogsByPage(any());
    }

    @Test
    void testGetExceptionLogCount() {
        when(exceptionLogService.count()).thenReturn(Mono.just(50L));
        
        ServerRequest request = MockServerRequest.builder().build();
        Mono<ServerResponse> response = logHandler.getExceptionLogCount(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(exceptionLogService).count();
    }
}