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 ProductObjectPermissionServiceTest extends BaseMockitoUnitTest {

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

    @Test
    void checkPermission_whenMemberOnlyAndCurrentUserIsMember_shouldPass() {
        Long productId = 1001L;
        Long loginUserId = 2001L;
        when(userObjectRoleMapper.selectActiveByObjectAndUserId("product", productId, loginUserId))
                .thenReturn(createMember(productId, loginUserId, 3001L));

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

        verifyNoInteractions(objectPermissionApi);
    }

    @Test
    void checkPermission_whenCurrentRolePermissionsContainTarget_shouldPass() {
        Long productId = 1002L;
        Long loginUserId = 2002L;
        when(userObjectRoleMapper.selectActiveByObjectAndUserId("product", productId, loginUserId))
                .thenReturn(createMember(productId, loginUserId, 3002L));
        when(objectPermissionApi.getObjectRolePermissions(3002L,
                PermissionScopeTypeEnum.OBJECT.getScopeType(), "product"))
                .thenReturn(success(Set.of("project:product:query")));

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

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

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

    @Test
    void checkPermission_whenCurrentUserIsNotMember_shouldThrowException() {
        Long productId = 1004L;
        Long loginUserId = 2004L;
        when(userObjectRoleMapper.selectActiveByObjectAndUserId("product", productId, loginUserId))
                .thenReturn(null);

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

        verifyNoInteractions(objectPermissionApi);
    }

    private UserObjectRoleDO createMember(Long productId, Long loginUserId, Long roleId) {
        UserObjectRoleDO member = new UserObjectRoleDO();
        member.setId(9001L);
        member.setUserId(loginUserId);
        member.setObjectType("product");
        member.setObjectId(productId);
        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;
    }

}
