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

import com.njcn.rdms.framework.test.core.ut.BaseMockitoUnitTest;
import com.njcn.rdms.module.project.framework.security.annotation.CheckObjectPermission;
import com.njcn.rdms.module.project.framework.security.service.ObjectPermissionService;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class ObjectPermissionAspectTest extends BaseMockitoUnitTest {

    @Mock
    private ObjectPermissionService objectPermissionService;

    @Test
    void around_shouldResolveProductIdFromMethodArgument() {
        when(objectPermissionService.getObjectType()).thenReturn("product");
        DemoService proxy = createProxy();

        String result = proxy.getProduct(1001L);

        assertEquals("ok", result);
        verify(objectPermissionService, times(1))
                .checkPermission(1001L, "project:product:query", false);
    }

    @Test
    void around_shouldResolveProductIdFromReqVoAndPassMemberOnlyFlag() {
        when(objectPermissionService.getObjectType()).thenReturn("product");
        DemoService proxy = createProxy();
        DemoReqVO reqVO = new DemoReqVO();
        reqVO.setId(1002L);

        String result = proxy.getProductContext(reqVO);

        assertEquals("context", result);
        verify(objectPermissionService, times(1))
                .checkPermission(1002L, "", true);
    }

    @Test
    void around_whenAccessible_shouldDispatchToCheckAccessible() {
        when(objectPermissionService.getObjectType()).thenReturn("product");
        DemoService proxy = createProxy();

        String result = proxy.getProductAccessible(1003L);

        assertEquals("accessible", result);
        verify(objectPermissionService, times(1)).checkAccessible(1003L);
        // accessible 模式不应再走权限码校验
        verify(objectPermissionService, never()).checkPermission(anyLong(), anyString(), anyBoolean());
    }

    private DemoService createProxy() {
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new DemoService());
        proxyFactory.addAspect(new ObjectPermissionAspect(java.util.List.of(objectPermissionService)));
        return proxyFactory.getProxy();
    }

    static class DemoService {

        @CheckObjectPermission(objectType = "product", objectId = "#productId", permission = "project:product:query")
        public String getProduct(Long productId) {
            return "ok";
        }

        @CheckObjectPermission(objectType = "product", objectId = "#reqVO.id", memberOnly = true)
        public String getProductContext(DemoReqVO reqVO) {
            return "context";
        }

        @CheckObjectPermission(objectType = "product", objectId = "#productId", accessible = true)
        public String getProductAccessible(Long productId) {
            return "accessible";
        }

    }

    static class DemoReqVO {

        private Long id;

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

    }

}
