package com.njcn.rdms.module.project.service.project;

import com.njcn.rdms.framework.common.pojo.PageParam;
import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils;
import com.njcn.rdms.framework.test.core.ut.BaseMockitoUnitTest;
import com.njcn.rdms.module.project.constant.ProjectObjectConstants;
import com.njcn.rdms.module.project.constant.ProjectTaskConstants;
import com.njcn.rdms.module.project.controller.admin.project.project.vo.myproject.MyProjectPageReqVO;
import com.njcn.rdms.module.project.controller.admin.project.project.vo.myproject.MyProjectParticipatedRespVO;
import com.njcn.rdms.module.project.dal.dataobject.member.UserObjectRoleDO;
import com.njcn.rdms.module.project.dal.dataobject.project.ProjectDO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusModelDO;
import com.njcn.rdms.module.project.dal.mysql.member.UserObjectRoleMapper;
import com.njcn.rdms.module.project.dal.mysql.project.ProjectMapper;
import com.njcn.rdms.module.project.dal.mysql.project.execution.ProjectExecutionMapper;
import com.njcn.rdms.module.project.dal.mysql.project.task.ProjectTaskMapper;
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusModelMapper;
import com.njcn.rdms.module.system.api.permission.ObjectPermissionApi;
import com.njcn.rdms.module.system.api.permission.dto.ObjectRoleRespDTO;
import com.njcn.rdms.module.system.api.user.AdminUserApi;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
import static java.util.Collections.emptyList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;

class MyProjectServiceImplTest extends BaseMockitoUnitTest {

    @InjectMocks
    private MyProjectServiceImpl myProjectService;
    @Mock
    private ProjectMapper projectMapper;
    @Mock
    private ProjectTaskMapper projectTaskMapper;
    @Mock
    private ProjectExecutionMapper projectExecutionMapper;
    @Mock
    private UserObjectRoleMapper userObjectRoleMapper;
    @Mock
    private ObjectStatusModelMapper objectStatusModelMapper;
    @Mock
    private ObjectPermissionApi objectPermissionApi;
    @Mock
    private AdminUserApi adminUserApi;

    private MyProjectPageReqVO allPageReq() {
        MyProjectPageReqVO reqVO = new MyProjectPageReqVO();
        reqVO.setPageNo(1);
        reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        return reqVO;
    }

    private UserObjectRoleDO role(Long id, Long objectId, Long roleId) {
        UserObjectRoleDO r = new UserObjectRoleDO();
        r.setId(id);
        r.setObjectType(ProjectObjectConstants.OBJECT_TYPE);
        r.setObjectId(objectId);
        r.setRoleId(roleId);
        r.setStatus(0);
        return r;
    }

    @Test
    void testParticipated_emptyWhenNoRoles() {
        try (MockedStatic<SecurityFrameworkUtils> mock = mockStatic(SecurityFrameworkUtils.class)) {
            mock.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(100L);
            when(userObjectRoleMapper.selectActiveListByObjectTypeAndUserId(
                    eq(ProjectObjectConstants.OBJECT_TYPE), eq(100L))).thenReturn(emptyList());

            PageResult<MyProjectParticipatedRespVO> result = myProjectService.getMyParticipatedPage(allPageReq());

            assertEquals(0L, result.getTotal());
            assertEquals(0, result.getList().size());
        }
    }

    @Test
    void testParticipated_assemblesRoleProgressAndTaskCount() {
        try (MockedStatic<SecurityFrameworkUtils> mock = mockStatic(SecurityFrameworkUtils.class)) {
            mock.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(100L);
            // 我在项目 2001 有两个角色行：roleId=10(manager) + roleId=20(dev)
            when(userObjectRoleMapper.selectActiveListByObjectTypeAndUserId(
                    eq(ProjectObjectConstants.OBJECT_TYPE), eq(100L)))
                    .thenReturn(List.of(role(1L, 2001L, 10L), role(2L, 2001L, 20L)));
            ProjectDO project = new ProjectDO();
            project.setId(2001L);
            project.setProjectName("商城 V2 升级");
            project.setProjectCode("MALL-V2");
            project.setStatusCode("active");
            project.setProgressRate(new BigDecimal("69.6"));
            when(projectMapper.selectBatchIds(anyCollection())).thenReturn(List.of(project));
            // statusName 回填
            ObjectStatusModelDO sm = new ObjectStatusModelDO();
            sm.setStatusCode("active");
            sm.setStatusName("进行中");
            when(objectStatusModelMapper.selectListByObjectTypeEnabled(ProjectObjectConstants.OBJECT_TYPE))
                    .thenReturn(List.of(sm));
            // 角色名：10=项目负责人(manager)，20=开发
            ObjectRoleRespDTO manager = new ObjectRoleRespDTO();
            manager.setId(10L);
            manager.setCode(ProjectObjectConstants.MANAGER_ROLE_CODE);
            manager.setName("项目负责人");
            ObjectRoleRespDTO dev = new ObjectRoleRespDTO();
            dev.setId(20L);
            dev.setCode("dev");
            dev.setName("开发");
            when(objectPermissionApi.getObjectRoleList(anyCollection(), any(), any()))
                    .thenReturn(success(List.of(manager, dev)));
            // 终态集 + 任务计数
            when(objectStatusModelMapper.selectTerminalStatusCodesByObjectTypeEnabled(ProjectTaskConstants.OBJECT_TYPE))
                    .thenReturn(List.of("completed", "cancelled"));
            when(projectTaskMapper.selectMyTaskCountGroupByProjectIds(eq(100L), anyCollection(), anyCollection()))
                    .thenReturn(List.<Map<String, Object>>of(Map.of(
                            "projectId", 2001L, "totalCount", 8L, "pendingCount", 3L)));

            PageResult<MyProjectParticipatedRespVO> result = myProjectService.getMyParticipatedPage(allPageReq());

            assertEquals(1L, result.getTotal());
            MyProjectParticipatedRespVO vo = result.getList().get(0);
            assertEquals(2001L, vo.getId());
            assertEquals("MALL-V2", vo.getCode());
            assertEquals("进行中", vo.getStatusName());
            assertEquals(70, vo.getProgress());               // 69.6 → HALF_UP → 70
            assertEquals("项目负责人 / 开发", vo.getMyRole());   // manager 主 + 附加
            assertEquals(8, vo.getMyTaskCount());
            assertEquals(3, vo.getMyPendingTaskCount());
        }
    }

    @Test
    void testParticipated_excludesProjectWhenOnlyInvisibleRole() {
        try (MockedStatic<SecurityFrameworkUtils> mock = mockStatic(SecurityFrameworkUtils.class)) {
            mock.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(100L);
            // 我在项目 2001 只有一个隐式角色 roleId=90（visible=0，如创建者），无任何可见角色
            when(userObjectRoleMapper.selectActiveListByObjectTypeAndUserId(
                    eq(ProjectObjectConstants.OBJECT_TYPE), eq(100L)))
                    .thenReturn(List.of(role(1L, 2001L, 90L)));
            ProjectDO project = new ProjectDO();
            project.setId(2001L);
            project.setProjectName("商城 V2 升级");
            project.setProjectCode("MALL-V2");
            project.setStatusCode("active");
            when(projectMapper.selectBatchIds(anyCollection())).thenReturn(List.of(project));
            when(objectStatusModelMapper.selectListByObjectTypeEnabled(ProjectObjectConstants.OBJECT_TYPE))
                    .thenReturn(emptyList());
            // 角色 90：创建者，visible=0
            ObjectRoleRespDTO creator = new ObjectRoleRespDTO();
            creator.setId(90L);
            creator.setCode("project_creator");
            creator.setName("创建者");
            creator.setVisible(0);
            when(objectPermissionApi.getObjectRoleList(anyCollection(), any(), any()))
                    .thenReturn(success(List.of(creator)));
            when(objectStatusModelMapper.selectTerminalStatusCodesByObjectTypeEnabled(ProjectTaskConstants.OBJECT_TYPE))
                    .thenReturn(List.of("completed", "cancelled"));
            when(projectTaskMapper.selectMyTaskCountGroupByProjectIds(eq(100L), anyCollection(), anyCollection()))
                    .thenReturn(emptyList());

            PageResult<MyProjectParticipatedRespVO> result = myProjectService.getMyParticipatedPage(allPageReq());

            // 仅隐式角色 → 该项目整项剔除
            assertEquals(0L, result.getTotal());
            assertEquals(0, result.getList().size());
        }
    }

    @Test
    void testOwned_emptyWhenNoManagedProjects() {
        try (MockedStatic<SecurityFrameworkUtils> mock = mockStatic(SecurityFrameworkUtils.class)) {
            mock.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(100L);
            when(projectMapper.selectList(any(com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX.class)))
                    .thenReturn(emptyList());

            PageResult<com.njcn.rdms.module.project.controller.admin.project.project.vo.myproject.MyProjectOwnedRespVO>
                    result = myProjectService.getMyOwnedPage(allPageReq());

            assertEquals(0L, result.getTotal());
            assertEquals(0, result.getList().size());
        }
    }

    @Test
    void testOwned_assemblesCountsAndMembers() {
        try (MockedStatic<SecurityFrameworkUtils> mock = mockStatic(SecurityFrameworkUtils.class)) {
            mock.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(100L);
            ProjectDO project = new ProjectDO();
            project.setId(2001L);
            project.setProjectName("商城 V2 升级");
            project.setProjectCode("MALL-V2");
            project.setProgressRate(new BigDecimal("70.0"));
            when(projectMapper.selectList(any(com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX.class)))
                    .thenReturn(List.of(project));
            when(objectStatusModelMapper.selectTerminalStatusCodesByObjectTypeEnabled(ProjectTaskConstants.OBJECT_TYPE))
                    .thenReturn(List.of("completed", "cancelled"));
            when(objectStatusModelMapper.selectTerminalStatusCodesByObjectTypeEnabled(
                    com.njcn.rdms.module.project.constant.ProjectExecutionConstants.OBJECT_TYPE))
                    .thenReturn(List.of("completed", "cancelled"));
            when(projectTaskMapper.selectTaskAndOverdueCountGroupByProjectIds(anyCollection(), anyCollection(), any()))
                    .thenReturn(List.<Map<String, Object>>of(Map.of(
                            "projectId", 2001L, "taskCount", 24L, "overdueCount", 2L)));
            when(projectExecutionMapper.selectExecutionCountGroupByProjectIds(anyCollection(), anyCollection()))
                    .thenReturn(List.<Map<String, Object>>of(Map.of("projectId", 2001L, "executionCount", 6L)));
            when(projectTaskMapper.selectActiveTaskCountGroupByProjectIdAndOwner(anyCollection(), anyCollection()))
                    .thenReturn(List.<Map<String, Object>>of(
                            Map.of("projectId", 2001L, "ownerId", 101L, "activeTaskCount", 6L),
                            Map.of("projectId", 2001L, "ownerId", 102L, "activeTaskCount", 3L)));
            // 成员清单：101 / 102（102 出现两行多角色，去重后仍一个成员）
            UserObjectRoleDO m1 = role(11L, 2001L, 10L);
            m1.setUserId(101L);
            UserObjectRoleDO m2 = role(12L, 2001L, 20L);
            m2.setUserId(102L);
            UserObjectRoleDO m3 = role(13L, 2001L, 30L);
            m3.setUserId(102L);
            when(userObjectRoleMapper.selectActiveListByObjectTypeAndObjectIds(
                    eq(ProjectObjectConstants.OBJECT_TYPE), anyCollection()))
                    .thenReturn(List.of(m1, m2, m3));
            com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO u101 =
                    new com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO();
            u101.setId(101L);
            u101.setNickname("张三");
            com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO u102 =
                    new com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO();
            u102.setId(102L);
            u102.setNickname("李四");
            when(adminUserApi.getUserMap(anyCollection())).thenReturn(Map.of(101L, u101, 102L, u102));
            ObjectRoleRespDTO manager = new ObjectRoleRespDTO();
            manager.setName("项目负责人");
            when(objectPermissionApi.getObjectRoleByCode(eq(ProjectObjectConstants.MANAGER_ROLE_CODE), any(), any()))
                    .thenReturn(success(manager));

            PageResult<com.njcn.rdms.module.project.controller.admin.project.project.vo.myproject.MyProjectOwnedRespVO>
                    result = myProjectService.getMyOwnedPage(allPageReq());

            assertEquals(1L, result.getTotal());
            var vo = result.getList().get(0);
            assertEquals(70, vo.getProgress());
            assertEquals("项目负责人", vo.getMyRole());
            assertEquals(24, vo.getTaskCount());
            assertEquals(2, vo.getOverdueCount());
            assertEquals(6, vo.getExecutionCount());
            assertEquals(2, vo.getMemberCount());          // 101 + 102 去重
            assertEquals(2, vo.getMembers().size());
            assertEquals(6, vo.getMembers().get(0).getActiveTaskCount());  // 101 → 6
        }
    }
}
