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

import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils;
import com.njcn.rdms.framework.test.core.ut.BaseMockitoUnitTest;
import com.njcn.rdms.module.project.controller.admin.project.project.vo.workbench.TeamLoadRespVO;
import com.njcn.rdms.module.project.dal.dataobject.project.ProjectDO;
import com.njcn.rdms.module.project.dal.mysql.personal.PersonalItemMapper;
import com.njcn.rdms.module.project.dal.mysql.project.ProjectMapper;
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.user.AdminUserApi;
import com.njcn.rdms.module.system.api.user.UserManagementRelationApi;
import com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO;
import com.njcn.rdms.module.system.api.user.dto.UserManagementRelationRespDTO;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;

import java.time.LocalDateTime;
import java.util.HashMap;
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.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;

class MyTeamServiceImplTest extends BaseMockitoUnitTest {

    @InjectMocks
    private MyTeamServiceImpl myTeamService;
    @Mock
    private ProjectTaskMapper projectTaskMapper;
    @Mock
    private PersonalItemMapper personalItemMapper;
    @Mock
    private ProjectMapper projectMapper;
    @Mock
    private ObjectStatusModelMapper objectStatusModelMapper;
    @Mock
    private UserManagementRelationApi userManagementRelationApi;
    @Mock
    private AdminUserApi adminUserApi;

    private UserManagementRelationRespDTO relation(Long subordinateId,
                                                   LocalDateTime from, LocalDateTime until) {
        UserManagementRelationRespDTO dto = new UserManagementRelationRespDTO();
        dto.setSubordinateUserId(subordinateId);
        dto.setEffectiveFrom(from);
        dto.setEffectiveUntil(until);
        return dto;
    }

    private AdminUserRespDTO user(Long id, String nickname) {
        AdminUserRespDTO dto = new AdminUserRespDTO();
        dto.setId(id);
        dto.setNickname(nickname);
        return dto;
    }

    @Test
    void resolveTeamMembers_selfFirst_expiredRelationExcluded() {
        try (MockedStatic<SecurityFrameworkUtils> mock = mockStatic(SecurityFrameworkUtils.class)) {
            mock.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(100L);
            LocalDateTime now = LocalDateTime.now();
            when(userManagementRelationApi.getRelationListByManagerUserId(100L)).thenReturn(success(List.of(
                    relation(200L, null, null),                       // 生效中(无期限)
                    relation(300L, now.minusDays(1), now.minusHours(1)) // 已失效 → 排除
            )));
            Map<Long, AdminUserRespDTO> userMap = new HashMap<>();
            userMap.put(100L, user(100L, "我"));
            userMap.put(200L, user(200L, "张三"));
            when(adminUserApi.getUserMap(anyCollection())).thenReturn(userMap);
            List<MyTeamService.TeamMember> members = myTeamService.resolveTeamMembers();
            assertEquals(2, members.size());
            assertEquals(100L, members.get(0).userId()); // self first
            assertEquals("张三", members.get(1).nickname());
        }
    }

    @Test
    void getTeamLoad_noSubordinates_selfOnlyWithEmptyItems() {
        try (MockedStatic<SecurityFrameworkUtils> mock = mockStatic(SecurityFrameworkUtils.class)) {
            mock.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(100L);
            when(userManagementRelationApi.getRelationListByManagerUserId(100L)).thenReturn(success(emptyList()));
            when(adminUserApi.getUserMap(anyCollection()))
                    .thenReturn(Map.of(100L, user(100L, "我")));
            when(objectStatusModelMapper.selectTerminalStatusCodesByObjectTypeEnabled(anyString()))
                    .thenReturn(List.of("completed", "cancelled"));
            when(projectTaskMapper.selectInvolvedLoadStatsByUserIds(anyCollection(), anyCollection(), any(), any()))
                    .thenReturn(emptyList());
            when(personalItemMapper.selectLoadStatsByOwnerIds(anyCollection(), anyCollection(), any(), any()))
                    .thenReturn(emptyList());
            TeamLoadRespVO resp = myTeamService.getTeamLoad();
            assertEquals(1, resp.getMembers().size());
            assertEquals(100L, resp.getMembers().get(0).getUserId());
            assertEquals(0, resp.getMembers().get(0).getItems().size()); // 无任务也返回,items 空数组
            assertEquals(0, resp.getMembers().get(0).getDueSoonCount());
        }
    }

    /**
     * 多成员场景:排序、items 组装（任务行 + 个人事项行）、压力公式。
     *
     * <p>压力 = items 之和 + dueSoonCount + overdueCount
     * <ul>
     *   <li>用户 300：任务 5+1=6，dueSoon=0，overdue=2 → 压力 8</li>
     *   <li>用户 200：任务 2 + 个人 1=3，dueSoon=1，overdue=1 → 压力 5</li>
     * </ul>
     * 故排序：100(自己) → 300 → 200
     */
    @Test
    void getTeamLoad_multiMember_sortAndItemsAssembly() {
        try (MockedStatic<SecurityFrameworkUtils> mock = mockStatic(SecurityFrameworkUtils.class)) {
            mock.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(100L);

            // --- 下级关系：200 和 300 均生效中（无期限限制）---
            LocalDateTime now = LocalDateTime.now();
            when(userManagementRelationApi.getRelationListByManagerUserId(100L)).thenReturn(success(List.of(
                    relation(200L, null, null),
                    relation(300L, now.minusDays(10), null)
            )));

            // --- 用户基础信息 ---
            Map<Long, AdminUserRespDTO> userMap = new HashMap<>();
            userMap.put(100L, user(100L, "我"));
            userMap.put(200L, user(200L, "张三"));
            userMap.put(300L, user(300L, "李四"));
            when(adminUserApi.getUserMap(anyCollection())).thenReturn(userMap);

            // --- 终态码（与其他用例保持一致）---
            when(objectStatusModelMapper.selectTerminalStatusCodesByObjectTypeEnabled(anyString()))
                    .thenReturn(List.of("completed", "cancelled"));

            // --- 任务聚合：3 行（userId/projectId/taskCount/dueSoonCount/overdueCount）---
            // 键名以被测类 row.get(...) 实际读取键为准
            Map<String, Object> taskRow200P10 = Map.of(
                    "userId", 200L,
                    "projectId", 10L,
                    "taskCount", 2L,
                    "dueSoonCount", 1L,
                    "overdueCount", 0L
            );
            Map<String, Object> taskRow300P10 = Map.of(
                    "userId", 300L,
                    "projectId", 10L,
                    "taskCount", 5L,
                    "dueSoonCount", 0L,
                    "overdueCount", 2L
            );
            Map<String, Object> taskRow300P20 = Map.of(
                    "userId", 300L,
                    "projectId", 20L,
                    "taskCount", 1L,
                    "dueSoonCount", 0L,
                    "overdueCount", 0L
            );
            when(projectTaskMapper.selectInvolvedLoadStatsByUserIds(anyCollection(), anyCollection(), any(), any()))
                    .thenReturn(List.of(taskRow200P10, taskRow300P10, taskRow300P20));

            // --- 个人事项聚合：只有用户 200（键名以被测类 personal.get(...) 实际读取键为准）---
            Map<String, Object> personalRow200 = Map.of(
                    "ownerId", 200L,
                    "itemCount", 1L,
                    "dueSoonCount", 0L,
                    "overdueCount", 1L
            );
            when(personalItemMapper.selectLoadStatsByOwnerIds(anyCollection(), anyCollection(), any(), any()))
                    .thenReturn(List.of(personalRow200));

            // --- 项目名批量回填 ---
            ProjectDO project10 = new ProjectDO();
            project10.setId(10L);
            project10.setProjectName("收银台 V3");
            ProjectDO project20 = new ProjectDO();
            project20.setId(20L);
            project20.setProjectName("中台");
            when(projectMapper.selectBatchIds(anyCollection())).thenReturn(List.of(project10, project20));

            // ===== 执行 =====
            TeamLoadRespVO resp = myTeamService.getTeamLoad();
            List<TeamLoadRespVO.MemberVO> members = resp.getMembers();

            // --- 成员数量 ---
            assertEquals(3, members.size());

            // --- members[0]：自己，items 空 ---
            assertEquals(100L, members.get(0).getUserId());
            assertEquals(0, members.get(0).getItems().size());

            // --- members[1]：300（压力 8 > 200 的压力 5，排前）---
            assertEquals(300L, members.get(1).getUserId());
            List<TeamLoadRespVO.LoadItemVO> items300 = members.get(1).getItems();
            assertEquals(2, items300.size());
            // 项目行按 count 降序：项目 10(count=5) 在前，项目 20(count=1) 在后
            assertEquals("project", items300.get(0).getKind());
            assertEquals(10L, items300.get(0).getProjectId());
            assertEquals("收银台 V3", items300.get(0).getProjectName());
            assertEquals(5, items300.get(0).getCount());
            assertEquals("project", items300.get(1).getKind());
            assertEquals(20L, items300.get(1).getProjectId());
            assertEquals("中台", items300.get(1).getProjectName());
            assertEquals(1, items300.get(1).getCount());
            // 300 没有个人事项行
            assertEquals(0, members.get(1).getDueSoonCount());
            assertEquals(2, members.get(1).getOverdueCount());

            // --- members[2]：200（压力 5）---
            assertEquals(200L, members.get(2).getUserId());
            List<TeamLoadRespVO.LoadItemVO> items200 = members.get(2).getItems();
            // 1 行 project + 1 行 personal（项目行降序后，personal 固定追加在末尾）
            assertEquals(2, items200.size());
            assertEquals("project", items200.get(0).getKind());
            assertEquals(10L, items200.get(0).getProjectId());
            assertEquals("收银台 V3", items200.get(0).getProjectName());
            assertEquals(2, items200.get(0).getCount());
            assertEquals("personal", items200.get(1).getKind());
            assertNull(items200.get(1).getProjectId());
            assertNull(items200.get(1).getProjectName());
            assertEquals(1, items200.get(1).getCount());
            // dueSoon：任务行 1 + 个人 0 = 1；overdue：任务行 0 + 个人 1 = 1
            assertEquals(1, members.get(2).getDueSoonCount());
            assertEquals(1, members.get(2).getOverdueCount());
        }
    }

}
