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

import cn.novalon.manage.sys.core.domain.SysUser;
import cn.novalon.manage.sys.core.service.ISysUserService;
import cn.novalon.manage.sys.dto.request.PasswordChangeRequest;
import cn.novalon.manage.sys.dto.request.UserRegisterRequest;
import cn.novalon.manage.sys.dto.request.UserUpdateRequest;
import cn.novalon.manage.sys.core.command.CreateUserCommand;
import cn.novalon.manage.sys.core.command.UpdateUserCommand;
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 jakarta.validation.Validator;
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 java.util.List;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class SysUserHandlerTest {

    @Mock
    private ISysUserService userService;
    
    @Mock
    private Validator validator;

    private SysUserHandler userHandler;
    private SysUser testUser;

    @BeforeEach
    void setUp() {
        userHandler = new SysUserHandler(userService, validator);
        
        testUser = new SysUser();
        testUser.setId(1L);
        testUser.setUsername("testuser");
        testUser.setPassword("encoded_password");
        testUser.setEmail("test@example.com");
        testUser.setRoleId(1L);
        testUser.setStatus(1);
        testUser.setCreatedAt(LocalDateTime.now());
        testUser.setUpdatedAt(LocalDateTime.now());
    }

    @Test
    void testGetAllUsers() {
        when(userService.findAll(anyBoolean())).thenReturn(Flux.just(testUser));
        
        ServerRequest request = MockServerRequest.builder().build();
        Mono<ServerResponse> response = userHandler.getAllUsers(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(userService).findAll(anyBoolean());
    }

    @Test
    void testGetAllUsers_WithPagination() {
        PageResponse<SysUser> pageResponse = new PageResponse<>();
        pageResponse.setContent(java.util.Collections.singletonList(testUser));
        pageResponse.setTotalElements(1L);
        pageResponse.setTotalPages(1);
        
        when(userService.findUsersByPage(any())).thenReturn(Mono.just(pageResponse));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("page", "0")
                .queryParam("size", "10")
                .build();
        Mono<ServerResponse> response = userHandler.getAllUsers(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(userService).findUsersByPage(any());
    }

    @Test
    void testGetAllUsers_WithOnlyPageParam() {
        PageResponse<SysUser> pageResponse = new PageResponse<>();
        pageResponse.setContent(java.util.Collections.singletonList(testUser));
        pageResponse.setTotalElements(1L);
        
        when(userService.findUsersByPage(any())).thenReturn(Mono.just(pageResponse));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("page", "0")
                .build();
        Mono<ServerResponse> response = userHandler.getAllUsers(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(userService).findUsersByPage(any());
    }

    @Test
    void testGetUserCount() {
        when(userService.count()).thenReturn(Mono.just(10L));
        
        ServerRequest request = MockServerRequest.builder().build();
        Mono<ServerResponse> response = userHandler.getUserCount(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(userService).count();
    }

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

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

    @Test
    void testGetUserByUsername() {
        when(userService.findByUsername("testuser")).thenReturn(Mono.just(testUser));
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("username", "testuser")
                .build();
        Mono<ServerResponse> response = userHandler.getUserByUsername(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(userService).findByUsername("testuser");
    }

    @Test
    void testDeleteUser() {
        when(userService.deleteUser(1L)).thenReturn(Mono.empty());
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "1")
                .build();
        Mono<ServerResponse> response = userHandler.deleteUser(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.NO_CONTENT)
                .verifyComplete();
        
        verify(userService).deleteUser(1L);
    }

    @Test
    void testChangePassword() {
        PasswordChangeRequest passwordRequest = new PasswordChangeRequest();
        passwordRequest.setOldPassword("oldpassword");
        passwordRequest.setNewPassword("newpassword");
        
        when(userService.changePassword(anyLong(), anyString(), anyString())).thenReturn(Mono.just(testUser));
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "1")
                .body(Mono.just(passwordRequest));
        Mono<ServerResponse> response = userHandler.changePassword(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(userService).changePassword(anyLong(), anyString(), anyString());
    }

    @Test
    void testLogicalDeleteUser() {
        when(userService.logicalDeleteUser(1L)).thenReturn(Mono.empty());
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "1")
                .build();
        Mono<ServerResponse> response = userHandler.logicalDeleteUser(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.NO_CONTENT)
                .verifyComplete();
        
        verify(userService).logicalDeleteUser(1L);
    }

    @Test
    void testRestoreUser() {
        when(userService.restoreUser(1L)).thenReturn(Mono.empty());
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "1")
                .build();
        Mono<ServerResponse> response = userHandler.restoreUser(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.NO_CONTENT)
                .verifyComplete();
        
        verify(userService).restoreUser(1L);
    }

    @Test
    void testCheckUsernameExists() {
        when(userService.existsByUsername("testuser")).thenReturn(Mono.just(true));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("username", "testuser")
                .build();
        Mono<ServerResponse> response = userHandler.checkUsernameExists(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(userService).existsByUsername("testuser");
    }

    @Test
    void testCheckEmailExists() {
        when(userService.existsByEmail("test@example.com")).thenReturn(Mono.just(true));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("email", "test@example.com")
                .build();
        Mono<ServerResponse> response = userHandler.checkEmailExists(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(userService).existsByEmail("test@example.com");
    }

    @Test
    void testGetUsersByPage() {
        cn.novalon.manage.common.dto.PageResponse<SysUser> pageResponse = 
            new cn.novalon.manage.common.dto.PageResponse<>();
        pageResponse.setContent(List.of(testUser));
        pageResponse.setTotalElements(1L);
        pageResponse.setTotalPages(1);
        pageResponse.setCurrentPage(0);
        pageResponse.setPageSize(10);
        
        when(userService.findUsersByPage(any(cn.novalon.manage.common.dto.PageRequest.class)))
                .thenReturn(Mono.just(pageResponse));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("page", "0")
                .queryParam("size", "10")
                .queryParam("sort", "id")
                .queryParam("order", "asc")
                .build();
        Mono<ServerResponse> response = userHandler.getUsersByPage(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(userService).findUsersByPage(any(cn.novalon.manage.common.dto.PageRequest.class));
    }

    @Test
    void testGetUsersByPage_WithKeyword() {
        cn.novalon.manage.common.dto.PageResponse<SysUser> pageResponse = 
            new cn.novalon.manage.common.dto.PageResponse<>();
        pageResponse.setContent(List.of(testUser));
        pageResponse.setTotalElements(1L);
        
        when(userService.findUsersByPage(any(cn.novalon.manage.common.dto.PageRequest.class)))
                .thenReturn(Mono.just(pageResponse));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("page", "0")
                .queryParam("size", "10")
                .queryParam("keyword", "test")
                .build();
        Mono<ServerResponse> response = userHandler.getUsersByPage(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(userService).findUsersByPage(any(cn.novalon.manage.common.dto.PageRequest.class));
    }

    @Test
    void testCreateUser() {
        UserRegisterRequest registerRequest = new UserRegisterRequest();
        registerRequest.setUsername("newuser");
        registerRequest.setPassword("Password123!");
        registerRequest.setEmail("new@example.com");
        
        when(userService.createUser(any(CreateUserCommand.class))).thenReturn(Mono.just(testUser));
        
        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(registerRequest));
        Mono<ServerResponse> response = userHandler.createUser(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.CREATED)
                .verifyComplete();
        
        verify(userService).createUser(any(CreateUserCommand.class));
    }

    @Test
    void testUpdateUser() {
        UserUpdateRequest updateRequest = new UserUpdateRequest();
        updateRequest.setEmail("updated@example.com");
        updateRequest.setRoleId(2L);
        updateRequest.setStatus(0);
        
        when(userService.updateUser(any(UpdateUserCommand.class))).thenReturn(Mono.just(testUser));
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "1")
                .body(Mono.just(updateRequest));
        Mono<ServerResponse> response = userHandler.updateUser(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(userService).updateUser(any(UpdateUserCommand.class));
    }

    @Test
    void testUpdateUser_NotFound() {
        UserUpdateRequest updateRequest = new UserUpdateRequest();
        updateRequest.setEmail("updated@example.com");
        
        when(userService.updateUser(any(UpdateUserCommand.class))).thenReturn(Mono.empty());
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "999")
                .body(Mono.just(updateRequest));
        Mono<ServerResponse> response = userHandler.updateUser(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.NOT_FOUND)
                .verifyComplete();
        
        verify(userService).updateUser(any(UpdateUserCommand.class));
    }

    @Test
    void testLogicalDeleteUsers() {
        List<Long> ids = List.of(1L, 2L, 3L);
        when(userService.logicalDeleteUsers(anyList())).thenReturn(Mono.empty());
        
        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(ids));
        Mono<ServerResponse> response = userHandler.logicalDeleteUsers(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.NO_CONTENT)
                .verifyComplete();
        
        verify(userService).logicalDeleteUsers(anyList());
    }

    @Test
    void testRestoreUsers() {
        List<Long> ids = List.of(1L, 2L, 3L);
        when(userService.restoreUsers(anyList())).thenReturn(Mono.empty());
        
        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(ids));
        Mono<ServerResponse> response = userHandler.restoreUsers(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.NO_CONTENT)
                .verifyComplete();
        
        verify(userService).restoreUsers(anyList());
    }
}