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

import com.njcn.rdms.framework.test.core.ut.BaseMockitoUnitTest;
import com.njcn.rdms.module.project.dal.dataobject.project.ProjectDO;
import com.njcn.rdms.module.project.dal.mysql.project.ProjectMapper;
import com.njcn.rdms.module.project.dal.mysql.project.execution.ExecutionAssigneeMapper;
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.project.task.TaskAssigneeMapper;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;

import java.util.List;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;

/**
 * VisibilityScopeResolverImpl 单元测试。覆盖角色矩阵：
 * - 项目经理 → seesAll
 * - 非项目经理 → 4 源并集
 * - 非项目经理且无任何参与 → 空集合
 * - 执行维度同上
 */
class VisibilityScopeResolverImplTest extends BaseMockitoUnitTest {

    @InjectMocks
    private VisibilityScopeResolverImpl resolver;

    @Mock private ProjectMapper projectMapper;
    @Mock private ProjectExecutionMapper projectExecutionMapper;
    @Mock private ExecutionAssigneeMapper executionAssigneeMapper;
    @Mock private ProjectTaskMapper projectTaskMapper;
    @Mock private TaskAssigneeMapper taskAssigneeMapper;

    @Test
    void resolveForProject_managerShouldSeeAll() {
        Long projectId = 2001L, userId = 3001L;
        ProjectDO project = new ProjectDO();
        project.setId(projectId);
        project.setManagerUserId(userId);
        when(projectMapper.selectById(projectId)).thenReturn(project);

        VisibilityScope scope = resolver.resolveForProject(projectId, userId);

        assertTrue(scope.seesAll());
    }

    @Test
    void resolveForProject_nonManagerUnionsFourSources() {
        Long projectId = 2001L, userId = 3002L;
        ProjectDO project = new ProjectDO();
        project.setId(projectId);
        project.setManagerUserId(9999L);
        when(projectMapper.selectById(projectId)).thenReturn(project);

        when(projectExecutionMapper.selectIdsByProjectIdAndOwnerId(projectId, userId))
                .thenReturn(List.of(5001L));
        when(executionAssigneeMapper.selectActiveExecutionIdsByProjectIdAndUserId(projectId, userId))
                .thenReturn(List.of(5002L));
        when(projectTaskMapper.selectOwnedTaskAndDescendantIdsByProjectIdAndUserId(projectId, userId))
                .thenReturn(List.of(9001L, 9002L));
        when(taskAssigneeMapper.selectActiveTaskIdsByProjectIdAndUserId(projectId, userId))
                .thenReturn(List.of(9003L));

        VisibilityScope scope = resolver.resolveForProject(projectId, userId);

        assertFalse(scope.seesAll());
        assertEquals(Set.of(5001L, 5002L), scope.executionIds());
        assertEquals(Set.of(9001L, 9002L, 9003L), scope.taskIds());
    }

    @Test
    void resolveForProject_nonParticipantReturnsEmpty() {
        Long projectId = 2001L, userId = 3099L;
        ProjectDO project = new ProjectDO();
        project.setId(projectId);
        project.setManagerUserId(9999L);
        when(projectMapper.selectById(projectId)).thenReturn(project);
        when(projectExecutionMapper.selectIdsByProjectIdAndOwnerId(projectId, userId)).thenReturn(List.of());
        when(executionAssigneeMapper.selectActiveExecutionIdsByProjectIdAndUserId(projectId, userId)).thenReturn(List.of());
        when(projectTaskMapper.selectOwnedTaskAndDescendantIdsByProjectIdAndUserId(projectId, userId)).thenReturn(List.of());
        when(taskAssigneeMapper.selectActiveTaskIdsByProjectIdAndUserId(projectId, userId)).thenReturn(List.of());

        VisibilityScope scope = resolver.resolveForProject(projectId, userId);

        assertFalse(scope.seesAll());
        assertTrue(scope.executionIds().isEmpty());
        assertTrue(scope.taskIds().isEmpty());
    }

    @Test
    void resolveForExecution_managerShouldSeeAll() {
        Long projectId = 2001L, executionId = 5001L, userId = 3001L;
        ProjectDO project = new ProjectDO();
        project.setManagerUserId(userId);
        when(projectMapper.selectById(projectId)).thenReturn(project);

        VisibilityScope scope = resolver.resolveForExecution(projectId, executionId, userId);

        assertTrue(scope.seesAll());
    }

    @Test
    void resolveForExecution_nonManagerScopedToThatExecution() {
        Long projectId = 2001L, executionId = 5001L, userId = 3002L;
        ProjectDO project = new ProjectDO();
        project.setManagerUserId(9999L);
        when(projectMapper.selectById(projectId)).thenReturn(project);

        when(projectTaskMapper.selectOwnedTaskAndDescendantIdsByExecutionIdAndUserId(projectId, executionId, userId))
                .thenReturn(List.of(9001L));
        when(taskAssigneeMapper.selectActiveTaskIdsByProjectIdAndExecutionIdAndUserId(projectId, executionId, userId))
                .thenReturn(List.of(9002L));

        VisibilityScope scope = resolver.resolveForExecution(projectId, executionId, userId);

        assertFalse(scope.seesAll());
        assertTrue(scope.executionIds().isEmpty());
        assertEquals(Set.of(9001L, 9002L), scope.taskIds());
    }
}
