package cn.novalon.gym.manage.sys.handler.role;

import cn.novalon.gym.manage.sys.core.domain.SysRole;
import cn.novalon.gym.manage.sys.core.service.ISysRoleService;
import cn.novalon.gym.manage.sys.dto.request.RoleCreateRequest;
import cn.novalon.gym.manage.sys.dto.request.RoleUpdateRequest;
import cn.novalon.gym.manage.sys.core.command.CreateRoleCommand;
import cn.novalon.gym.manage.sys.core.command.UpdateRoleCommand;
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 static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class SysRoleHandlerTest {

    @Mock
    private ISysRoleService roleService;
    
    @Mock
    private Validator validator;

    private SysRoleHandler roleHandler;
    private SysRole testRole;

    @BeforeEach
    void setUp() {
        roleHandler = new SysRoleHandler(roleService, validator);
        
        testRole = new SysRole();
        testRole.setId(1L);
        testRole.setRoleName("ADMIN");
        testRole.setRoleKey("admin");
        testRole.setRoleSort(1);
        testRole.setStatus(1);
        testRole.setCreatedAt(LocalDateTime.now());
        testRole.setUpdatedAt(LocalDateTime.now());
    }

    @Test
    void testGetAllRoles() {
        when(roleService.findAll()).thenReturn(Flux.just(testRole));
        
        ServerRequest request = MockServerRequest.builder().build();
        Mono<ServerResponse> response = roleHandler.getAllRoles(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(roleService).findAll();
    }

    @Test
    void testGetRolesByPage() {
        cn.novalon.gym.manage.common.dto.PageResponse<SysRole> pageResponse = 
            new cn.novalon.gym.manage.common.dto.PageResponse<>();
        pageResponse.setContent(java.util.List.of(testRole));
        pageResponse.setTotalElements(1L);
        pageResponse.setTotalPages(1);
        pageResponse.setCurrentPage(0);
        pageResponse.setPageSize(10);
        
        when(roleService.findRolesByPage(any(cn.novalon.gym.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 = roleHandler.getRolesByPage(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(roleService).findRolesByPage(any(cn.novalon.gym.manage.common.dto.PageRequest.class));
    }

    @Test
    void testGetRolesByPage_WithKeyword() {
        cn.novalon.gym.manage.common.dto.PageResponse<SysRole> pageResponse = 
            new cn.novalon.gym.manage.common.dto.PageResponse<>();
        pageResponse.setContent(java.util.List.of(testRole));
        pageResponse.setTotalElements(1L);
        
        when(roleService.findRolesByPage(any(cn.novalon.gym.manage.common.dto.PageRequest.class)))
                .thenReturn(Mono.just(pageResponse));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("page", "0")
                .queryParam("size", "10")
                .queryParam("keyword", "admin")
                .build();
        Mono<ServerResponse> response = roleHandler.getRolesByPage(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(roleService).findRolesByPage(any(cn.novalon.gym.manage.common.dto.PageRequest.class));
    }

    @Test
    void testGetRoleCount() {
        when(roleService.count()).thenReturn(Mono.just(5L));
        
        ServerRequest request = MockServerRequest.builder().build();
        Mono<ServerResponse> response = roleHandler.getRoleCount(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(roleService).count();
    }

    @Test
    void testGetRoleByName() {
        when(roleService.findByRoleName("ADMIN")).thenReturn(Mono.just(testRole));
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("roleName", "ADMIN")
                .build();
        Mono<ServerResponse> response = roleHandler.getRoleByName(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(roleService).findByRoleName("ADMIN");
    }

    @Test
    void testGetRoleByName_NotFound() {
        when(roleService.findByRoleName("UNKNOWN")).thenReturn(Mono.empty());
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("roleName", "UNKNOWN")
                .build();
        Mono<ServerResponse> response = roleHandler.getRoleByName(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.NOT_FOUND)
                .verifyComplete();
        
        verify(roleService).findByRoleName("UNKNOWN");
    }

    @Test
    void testCheckNameExists() {
        when(roleService.existsByRoleName("ADMIN")).thenReturn(Mono.just(true));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("name", "ADMIN")
                .build();
        Mono<ServerResponse> response = roleHandler.checkNameExists(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(roleService).existsByRoleName("ADMIN");
    }

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

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

    @Test
    void testCreateRole() {
        RoleCreateRequest createRequest = new RoleCreateRequest();
        createRequest.setRoleName("NEW_ROLE");
        createRequest.setRoleKey("new_role");
        createRequest.setRoleSort(2);
        createRequest.setStatus(1);
        
        when(roleService.createRole(any(CreateRoleCommand.class))).thenReturn(Mono.just(testRole));
        
        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(createRequest));
        Mono<ServerResponse> response = roleHandler.createRole(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.CREATED)
                .verifyComplete();
        
        verify(roleService).createRole(any(CreateRoleCommand.class));
    }

    @Test
    void testUpdateRole() {
        RoleUpdateRequest updateRequest = new RoleUpdateRequest();
        updateRequest.setRoleName("UPDATED_ROLE");
        updateRequest.setRoleKey("updated_role");
        updateRequest.setRoleSort(3);
        updateRequest.setStatus(0);
        
        when(roleService.updateRole(any(UpdateRoleCommand.class))).thenReturn(Mono.just(testRole));
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "1")
                .body(Mono.just(updateRequest));
        Mono<ServerResponse> response = roleHandler.updateRole(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(roleService).updateRole(any(UpdateRoleCommand.class));
    }

    @Test
    void testUpdateRole_NotFound() {
        RoleUpdateRequest updateRequest = new RoleUpdateRequest();
        updateRequest.setRoleName("UPDATED_ROLE");
        
        when(roleService.updateRole(any(UpdateRoleCommand.class))).thenReturn(Mono.empty());
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "999")
                .body(Mono.just(updateRequest));
        Mono<ServerResponse> response = roleHandler.updateRole(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.NOT_FOUND)
                .verifyComplete();
        
        verify(roleService).updateRole(any(UpdateRoleCommand.class));
    }

    @Test
    void testDeleteRole() {
        when(roleService.logicalDeleteRole(1L)).thenReturn(Mono.just(testRole));
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "1")
                .build();
        Mono<ServerResponse> response = roleHandler.deleteRole(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(roleService).logicalDeleteRole(1L);
    }

    @Test
    void testDeleteRole_NotFound() {
        when(roleService.logicalDeleteRole(999L)).thenReturn(Mono.empty());
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "999")
                .build();
        Mono<ServerResponse> response = roleHandler.deleteRole(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.NOT_FOUND)
                .verifyComplete();
        
        verify(roleService).logicalDeleteRole(999L);
    }

    @Test
    void testRestoreRole() {
        when(roleService.restoreRole(1L)).thenReturn(Mono.just(testRole));
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "1")
                .build();
        Mono<ServerResponse> response = roleHandler.restoreRole(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(roleService).restoreRole(1L);
    }

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