package com.cf.imes.module.system.controller.admin.permission;

import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.framework.security.test.WithMockLoginUser;
import com.cf.imes.framework.test.core.ut.BaseWebUnitTest;
import com.cf.imes.framework.web.config.ChenfengWebAutoConfiguration;
import com.cf.imes.module.infra.api.logger.ApiErrorLogApi;
import com.cf.imes.module.system.common.WithMockLoginUserExtension;
import com.cf.imes.module.system.controller.admin.permission.vo.menu.*;
import com.cf.imes.module.system.dal.dataobject.permission.MenuDO;
import com.cf.imes.module.system.dal.dataobject.permission.RoleDO;
import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO;
import com.cf.imes.module.system.enums.permission.MenuSourceTypeEnum;
import com.cf.imes.module.system.enums.permission.MenuTypeEnum;
import com.cf.imes.module.system.service.permission.MenuService;
import com.cf.imes.module.system.service.permission.PermissionService;
import com.cf.imes.module.system.service.permission.RoleService;
import com.cf.imes.module.system.service.user.AdminUserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;

import java.util.*;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

/**
 * 菜单管理单元测试
 *
 * @author Gqr
 * @since 2026/1/26 16:40
 */
@WebMvcTest(controllers = MenuController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
@ExtendWith(WithMockLoginUserExtension.class)
@Import(ChenfengWebAutoConfiguration.class)
@TestPropertySource(properties = {
        "spring.application.name=test-app"
})
class MenuControllerTest extends BaseWebUnitTest {

    @Autowired
    private MockMvc mockMvc;

    @MockitoBean
    private MenuService menuService;

    @MockitoBean
    private PermissionService permissionService;

    @MockitoBean
    private AdminUserService userService;

    @MockitoBean
    private RoleService roleService;

    @Autowired
    private ObjectMapper objectMapper;

    @MockitoBean
    private ApiErrorLogApi apiErrorLogApi;

    @Test
    void createMenu_success() throws Exception {
        MenuSaveVO reqVO = new MenuSaveVO();
        reqVO.setName("新增菜单");
        reqVO.setParentId(0L);
        reqVO.setType(MenuTypeEnum.MENU.getType());
        reqVO.setSort(1);
        reqVO.setParentId(MenuDO.ID_ROOT);
        reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
        reqVO.setSourceType(MenuSourceTypeEnum.PRODUCTION.getType());

        when(menuService.createMenu(any(MenuSaveVO.class)))
                .thenReturn(100L);

        mockMvc.perform(post("/admin-api/system/menu/create")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(reqVO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data").value(100L));
    }

    @Test
    void updateMenu_success() throws Exception {
        MenuSaveVO reqVO = new MenuSaveVO();
        reqVO.setId(1L);
        reqVO.setName("更新菜单");
        reqVO.setType(MenuTypeEnum.MENU.getType());
        reqVO.setSort(1);
        reqVO.setParentId(MenuDO.ID_ROOT);
        reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
        reqVO.setSourceType(MenuSourceTypeEnum.PRODUCTION.getType());

        mockMvc.perform(put("/admin-api/system/menu/update")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(reqVO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data").value(true));

        verify(menuService).updateMenu(any(MenuSaveVO.class));
    }

    @Test
    void deleteMenu_success() throws Exception {
        mockMvc.perform(delete("/admin-api/system/menu/delete")
                        .param("id", "1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data").value(true));

        verify(menuService).deleteMenu(1L);
    }

    @Test
    @WithMockLoginUser(userId = 1L, organId = 100L)
    void getCustomMenuList_success() throws Exception {
        // 准备模拟数据
        List<MenuDO> menuList = Arrays.asList(createMenuDO(1L, CommonStatusEnum.ENABLE.getStatus()));


        AdminUserDO user = new AdminUserDO();
        user.setId(1L);

        Set<Long> roleIds = new HashSet<>();
        roleIds.add(1L);

        List<RoleDO> roles = Arrays.asList(createRoleDO(1L, CommonStatusEnum.ENABLE.getStatus()));

        Set<Long> menuIds = new HashSet<>();
        menuIds.add(1L);

        when(userService.getUser(1L)).thenReturn(user);
        when(permissionService.getUserRoleIdListByUserId(1L)).thenReturn(roleIds);
        when(roleService.getRoleList1(roleIds)).thenReturn(roles);
        when(permissionService.getRoleMenuListByRoleId2(any(Set.class), eq(100L))).thenReturn(menuIds);
        when(menuService.getCustomEndPointMenuList(menuIds)).thenReturn(menuList);

        mockMvc.perform(get("/admin-api/system/menu/custom/list"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data").isArray());

        verify(userService).getUser(1L);
        verify(permissionService).getUserRoleIdListByUserId(1L);
        verify(roleService).getRoleList1(roleIds);
        verify(permissionService).getRoleMenuListByRoleId2(any(Set.class), eq(100L));
        verify(menuService).getCustomEndPointMenuList(menuIds);
    }

    @Test
    @WithMockLoginUser(userId = 1L, organId = 100L, isSuperAdmin = true)
    void getMenuList_superAdmin_success() throws Exception {
        // 准备模拟数据
        List<MenuDO> menuList = Arrays.asList(createMenuDO(1L, CommonStatusEnum.ENABLE.getStatus()));

        when(menuService.getMenuList(any(MenuListReqVO.class))).thenReturn(menuList);

        mockMvc.perform(get("/admin-api/system/menu/list"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data").isArray());

        verify(menuService).getMenuList(any(MenuListReqVO.class));
    }

    @Test
    @WithMockLoginUser(userId = 1L, organId = 100L)
    void getMenuList_notsuperAdmin_getUserNull() throws Exception {
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        when(userService.getUser(loginUserId)).thenReturn(null);

        mockMvc.perform(get("/admin-api/system/menu/list"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data").isEmpty());
    }

    @Test
    @WithMockLoginUser(userId = 1L, organId = 100L)
    void getMenuList_notsuperAdmin_getRolesEmpty() throws Exception {
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        when(userService.getUser(loginUserId)).thenReturn(new AdminUserDO());

        when(permissionService.getUserRoleIdListByUserId(loginUserId)).thenReturn(null);

        mockMvc.perform(get("/admin-api/system/menu/list"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data").isEmpty());
    }

    @Test
    @WithMockLoginUser(userId = 1L, organId = 100L)
    void getMenuList_normalUser_success() throws Exception {
        // 准备模拟数据
        List<MenuDO> menuList = Arrays.asList(createMenuDO(1L, CommonStatusEnum.ENABLE.getStatus()));

        AdminUserDO user = new AdminUserDO();
        user.setId(1L);

        Set<Long> roleIds = new HashSet<>();
        roleIds.add(1L);

        List<RoleDO> roles = Arrays.asList(createRoleDO(1L, CommonStatusEnum.ENABLE.getStatus()));

        Set<Long> menuIds = new HashSet<>();
        menuIds.add(1L);

        when(userService.getUser(1L)).thenReturn(user);
        when(permissionService.getUserRoleIdListByUserId(1L)).thenReturn(roleIds);
        when(roleService.getRoleList1(roleIds)).thenReturn(roles);
        when(permissionService.getRoleMenuListByRoleId2(any(Set.class), eq(100L))).thenReturn(menuIds);
        when(menuService.getMenuList1(menuIds)).thenReturn(menuList);

        mockMvc.perform(get("/admin-api/system/menu/list"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data").isArray());

        verify(userService).getUser(1L);
        verify(permissionService).getUserRoleIdListByUserId(1L);
        verify(roleService).getRoleList1(roleIds);
        verify(permissionService).getRoleMenuListByRoleId2(any(Set.class), eq(100L));
        verify(menuService).getMenuList1(menuIds);
    }

    @Test
    void getMenuList_noLoginUser_failure() throws Exception {
        mockMvc.perform(get("/admin-api/system/menu/list"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(GlobalErrorCodeConstants.UNAUTHORIZED.getCode()));
    }

    @Test
    void getCustomMenuList_noLoginUser_failure() throws Exception {
        mockMvc.perform(get("/admin-api/system/menu/custom/list"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(GlobalErrorCodeConstants.UNAUTHORIZED.getCode()));
    }

    @Test
    @WithMockLoginUser
    void getCustomMenuList_getUserNull() throws Exception {
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        when(userService.getUser(loginUserId)).thenReturn(null);

        mockMvc.perform(get("/admin-api/system/menu/custom/list"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").isEmpty());
    }

    @Test
    @WithMockLoginUser
    void getCustomMenuList_getRolesEmpty() throws Exception {
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        when(userService.getUser(loginUserId)).thenReturn(new AdminUserDO());

        when(permissionService.getUserRoleIdListByUserId(loginUserId)).thenReturn(null);

        mockMvc.perform(get("/admin-api/system/menu/custom/list"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data").isEmpty());
    }

    @Test
    void getSimpleMenuList_success() throws Exception {
        List<MenuDO> menuList = Arrays.asList(createMenuDO(1L, CommonStatusEnum.ENABLE.getStatus()));
        when(menuService.getMenuListByOrgan(any(MenuListReqVO.class))).thenReturn(menuList);

        mockMvc.perform(get("/admin-api/system/menu/list-all-simple"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data").isArray());

        verify(menuService).getMenuListByOrgan(any(MenuListReqVO.class));
    }

    @Test
    void getMenu_success() throws Exception {
        MenuDO menuDO = createMenuDO(1L, CommonStatusEnum.ENABLE.getStatus());
        when(menuService.getMenu(1L)).thenReturn(menuDO);

        mockMvc.perform(get("/admin-api/system/menu/get")
                        .param("id", "1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data.id").value(1L));

        verify(menuService).getMenu(1L);
    }

    @Test
    void trans_success() throws Exception {
        MenuTransReqVO reqVO = new MenuTransReqVO();
        reqVO.setTargetLanguage("en");

        mockMvc.perform(post("/admin-api/system/menu/trans")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(reqVO)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(0))
                .andExpect(jsonPath("$.data").value(true));

        verify(menuService).trans(any(MenuTransReqVO.class));
    }

    // 辅助方法：创建MenuDO实例
    private MenuDO createMenuDO(Long id, Integer status) {
        MenuDO menuDO = new MenuDO();
        menuDO.setId(id);
        menuDO.setStatus(status);
        menuDO.setName("测试菜单");
        menuDO.setSort(0);
        return menuDO;
    }

    // 辅助方法：创建RoleDO实例
    private RoleDO createRoleDO(Long id, Integer status) {
        RoleDO roleDO = new RoleDO();
        roleDO.setId(id);
        roleDO.setStatus(status);
        return roleDO;
    }
}