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

import com.njcn.rdms.framework.common.exception.ServiceException;
import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils;
import com.njcn.rdms.framework.test.core.ut.BaseMockitoUnitTest;
import com.njcn.rdms.module.project.dal.dataobject.member.UserObjectRoleDO;
import com.njcn.rdms.module.project.dal.mysql.member.UserObjectRoleMapper;
import com.njcn.rdms.module.project.enums.ErrorCodeConstants;
import com.njcn.rdms.module.system.api.permission.ObjectPermissionApi;
import com.njcn.rdms.module.system.enums.permission.PermissionScopeTypeEnum;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;

import java.util.Set;

import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;

class ProjectObjectPermissionServiceTest extends BaseMockitoUnitTest {

    @InjectMocks
    private ProjectObjectPermissionService permissionService;
    @Mock
    private UserObjectRoleMapper userObjectRoleMapper;
    @Mock
    private ObjectPermissionApi objectPermissionApi;

    @Test
    void checkPermission_whenMemberOnlyAndActiveMember_shouldPass() {
        Long projectId = 1001L;
        Long loginUserId = 2001L;
        when(userObjectRoleMapper.selectActiveByObjectAndUserId("project", projectId, loginUserId))
                .thenReturn(createMember(projectId, loginUserId, 3001L));

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId)) {
            assertDoesNotThrow(() -> permissionService.checkPermission(projectId, null, true));
        }

        verifyNoInteractions(objectPermissionApi);
    }

    @Test
    void checkPermission_whenNoActiveMember_shouldThrowProjectPermissionDenied() {
        Long projectId = 1002L;
        Long loginUserId = 2002L;
        when(userObjectRoleMapper.selectActiveByObjectAndUserId("project", projectId, loginUserId))
                .thenReturn(null);

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId)) {
            ServiceException ex = assertThrows(ServiceException.class,
                    () -> permissionService.checkPermission(projectId, "project:project:update", false));
            assertEquals(ErrorCodeConstants.PROJECT_OBJECT_PERMISSION_DENIED.getCode(), ex.getCode());
        }
    }

    @Test
    void checkPermission_whenPermissionPresent_shouldPass() {
        Long projectId = 1003L;
        Long loginUserId = 2003L;
        when(userObjectRoleMapper.selectActiveByObjectAndUserId("project", projectId, loginUserId))
                .thenReturn(createMember(projectId, loginUserId, 3003L));
        when(objectPermissionApi.getObjectRolePermissions(3003L,
                PermissionScopeTypeEnum.OBJECT.getScopeType(), "project"))
                .thenReturn(success(Set.of("project:project:update")));

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId)) {
            assertDoesNotThrow(() -> permissionService.checkPermission(projectId, "project:project:update", false));
        }
    }

    @Test
    void checkPermission_whenPermissionMissing_shouldThrowProjectPermissionDenied() {
        Long projectId = 1004L;
        Long loginUserId = 2004L;
        when(userObjectRoleMapper.selectActiveByObjectAndUserId("project", projectId, loginUserId))
                .thenReturn(createMember(projectId, loginUserId, 3004L));
        when(objectPermissionApi.getObjectRolePermissions(3004L,
                PermissionScopeTypeEnum.OBJECT.getScopeType(), "project"))
                .thenReturn(success(Set.of("project:project:query")));

        try (MockedStatic<SecurityFrameworkUtils> mockedStatic = mockLoginUser(loginUserId)) {
            ServiceException ex = assertThrows(ServiceException.class,
                    () -> permissionService.checkPermission(projectId, "project:project:delete", false));
            assertEquals(ErrorCodeConstants.PROJECT_OBJECT_PERMISSION_DENIED.getCode(), ex.getCode());
        }
    }

    private UserObjectRoleDO createMember(Long projectId, Long loginUserId, Long roleId) {
        UserObjectRoleDO member = new UserObjectRoleDO();
        member.setId(9001L);
        member.setUserId(loginUserId);
        member.setObjectType("project");
        member.setObjectId(projectId);
        member.setRoleId(roleId);
        member.setStatus(0);
        return member;
    }

    private MockedStatic<SecurityFrameworkUtils> mockLoginUser(Long loginUserId) {
        MockedStatic<SecurityFrameworkUtils> mockedStatic = mockStatic(SecurityFrameworkUtils.class);
        mockedStatic.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(loginUserId);
        return mockedStatic;
    }

}
