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

import cn.novalon.gym.manage.sys.core.domain.SysMenu;
import cn.novalon.gym.manage.sys.core.service.ISysMenuService;
import cn.novalon.gym.manage.sys.dto.request.MenuCreateRequest;
import cn.novalon.gym.manage.sys.dto.request.MenuUpdateRequest;
import cn.novalon.gym.manage.sys.core.command.CreateMenuCommand;
import cn.novalon.gym.manage.sys.core.command.UpdateMenuCommand;
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 MenuHandlerTest {

    @Mock
    private ISysMenuService menuService;

    private MenuHandler menuHandler;
    private SysMenu testMenu;

    @BeforeEach
    void setUp() {
        menuHandler = new MenuHandler(menuService);
        
        testMenu = new SysMenu();
        testMenu.setId(1L);
        testMenu.setParentId(0L);
        testMenu.setMenuName("系统管理");
        testMenu.setMenuType("M");
        testMenu.setOrderNum(1);
        testMenu.setComponent("system");
        testMenu.setPerms("system:manage");
        testMenu.setStatus(1);
        testMenu.setCreatedAt(LocalDateTime.now());
        testMenu.setUpdatedAt(LocalDateTime.now());
    }

    @Test
    void testGetAllMenus() {
        when(menuService.findAll()).thenReturn(Flux.just(testMenu));
        
        ServerRequest request = MockServerRequest.builder().build();
        Mono<ServerResponse> response = menuHandler.getAllMenus(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(menuService).findAll();
    }

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

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

    @Test
    void testGetMenuTree() {
        when(menuService.findAll()).thenReturn(Flux.just(testMenu));
        when(menuService.buildMenuTree(any())).thenReturn(Flux.just(testMenu));
        
        ServerRequest request = MockServerRequest.builder().build();
        Mono<ServerResponse> response = menuHandler.getMenuTree(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(menuService).findAll();
        verify(menuService).buildMenuTree(any());
    }

    @Test
    void testGetMenusByParent() {
        when(menuService.findByParentId(0L)).thenReturn(Flux.just(testMenu));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("parentId", "0")
                .build();
        Mono<ServerResponse> response = menuHandler.getMenusByParent(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(menuService).findByParentId(0L);
    }

    @Test
    void testGetMenusByParent_Default() {
        when(menuService.findByParentId(0L)).thenReturn(Flux.just(testMenu));
        
        ServerRequest request = MockServerRequest.builder()
                .build();
        Mono<ServerResponse> response = menuHandler.getMenusByParent(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(menuService).findByParentId(0L);
    }

    @Test
    void testGetMenusByType() {
        SysMenu menu1 = new SysMenu();
        menu1.setId(1L);
        menu1.setMenuName("系统管理");
        menu1.setMenuType("M");
        
        SysMenu menu2 = new SysMenu();
        menu2.setId(2L);
        menu2.setMenuName("用户管理");
        menu2.setMenuType("C");
        
        when(menuService.findAll()).thenReturn(Flux.just(menu1, menu2));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("menuType", "M")
                .build();
        Mono<ServerResponse> response = menuHandler.getMenusByType(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(menuService).findAll();
    }

    @Test
    void testGetMenusByType_Null() {
        SysMenu menu1 = new SysMenu();
        menu1.setId(1L);
        menu1.setMenuName("系统管理");
        menu1.setMenuType("M");
        
        SysMenu menu2 = new SysMenu();
        menu2.setId(2L);
        menu2.setMenuName("用户管理");
        menu2.setMenuType("C");
        
        when(menuService.findAll()).thenReturn(Flux.just(menu1, menu2));
        
        ServerRequest request = MockServerRequest.builder()
                .build();
        Mono<ServerResponse> response = menuHandler.getMenusByType(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(menuService).findAll();
    }

    @Test
    void testGetMenusByType_NoMatch() {
        SysMenu menu1 = new SysMenu();
        menu1.setId(1L);
        menu1.setMenuName("系统管理");
        menu1.setMenuType("M");
        
        SysMenu menu2 = new SysMenu();
        menu2.setId(2L);
        menu2.setMenuName("用户管理");
        menu2.setMenuType("C");
        
        when(menuService.findAll()).thenReturn(Flux.just(menu1, menu2));
        
        ServerRequest request = MockServerRequest.builder()
                .queryParam("menuType", "F")
                .build();
        Mono<ServerResponse> response = menuHandler.getMenusByType(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(menuService).findAll();
    }

    @Test
    void testCreateMenu() {
        MenuCreateRequest createRequest = new MenuCreateRequest();
        createRequest.setParentId(0L);
        createRequest.setMenuName("新菜单");
        createRequest.setMenuType("M");
        createRequest.setOrderNum(2);
        createRequest.setComponent("new_menu");
        createRequest.setPerms("new:menu");
        createRequest.setStatus(1);
        
        when(menuService.createMenu(any(CreateMenuCommand.class))).thenReturn(Mono.just(testMenu));
        
        ServerRequest request = MockServerRequest.builder()
                .body(Mono.just(createRequest));
        Mono<ServerResponse> response = menuHandler.createMenu(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.CREATED)
                .verifyComplete();
        
        verify(menuService).createMenu(any(CreateMenuCommand.class));
    }

    @Test
    void testUpdateMenu() {
        MenuUpdateRequest updateRequest = new MenuUpdateRequest();
        updateRequest.setParentId(0L);
        updateRequest.setMenuName("更新菜单");
        updateRequest.setMenuType("M");
        updateRequest.setOrderNum(3);
        updateRequest.setComponent("updated_menu");
        updateRequest.setPerms("updated:menu");
        updateRequest.setStatus(1);
        
        when(menuService.updateMenu(any(UpdateMenuCommand.class))).thenReturn(Mono.just(testMenu));
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "1")
                .body(Mono.just(updateRequest));
        Mono<ServerResponse> response = menuHandler.updateMenu(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.OK)
                .verifyComplete();
        
        verify(menuService).updateMenu(any(UpdateMenuCommand.class));
    }

    @Test
    void testUpdateMenu_NotFound() {
        MenuUpdateRequest updateRequest = new MenuUpdateRequest();
        updateRequest.setMenuName("更新菜单");
        
        when(menuService.updateMenu(any(UpdateMenuCommand.class))).thenReturn(Mono.empty());
        
        ServerRequest request = MockServerRequest.builder()
                .pathVariable("id", "999")
                .body(Mono.just(updateRequest));
        Mono<ServerResponse> response = menuHandler.updateMenu(request);
        
        StepVerifier.create(response)
                .expectNextMatches(serverResponse -> 
                    serverResponse.statusCode() == HttpStatus.NOT_FOUND)
                .verifyComplete();
        
        verify(menuService).updateMenu(any(UpdateMenuCommand.class));
    }

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