package tech.easyflow.ai.service.impl;

import org.junit.Assert;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import tech.easyflow.ai.enums.KnowledgeApiPermissionScope;
import tech.easyflow.common.cache.RedisLockExecutor;
import tech.easyflow.system.entity.SysApiKey;
import tech.easyflow.system.entity.SysApiKeyResource;
import tech.easyflow.system.entity.SysApiKeyResourceMapping;
import tech.easyflow.system.service.SysApiKeyResourceMappingService;
import tech.easyflow.system.service.SysApiKeyResourceService;
import tech.easyflow.system.service.SysApiKeyService;

import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 知识库 Public API 产品权限映射测试。
 *
 * @author Codex
 * @since 2026-08-02
 */
public class KnowledgeSharePermissionServiceImplTest {

    /**
     * 验证读取、导入和维护权限分别只生成各自接口映射。
     *
     * @throws Exception 依赖注入失败
     */
    @Test
    public void replacePermissionsShouldKeepReadImportMaintenanceSeparated()
        throws Exception {
        BigInteger apiKeyId = BigInteger.valueOf(701);
        SysApiKeyService apiKeyService = Mockito.mock(SysApiKeyService.class);
        SysApiKeyResourceService resourceService =
            Mockito.mock(SysApiKeyResourceService.class);
        SysApiKeyResourceMappingService mappingService =
            Mockito.mock(SysApiKeyResourceMappingService.class);
        RedisLockExecutor redisLockExecutor =
            Mockito.mock(RedisLockExecutor.class);
        RedisLockExecutor.LockHandle lockHandle =
            Mockito.mock(RedisLockExecutor.LockHandle.class);
        Mockito.when(redisLockExecutor.tryAcquire(
            Mockito.anyString(),
            Mockito.any(),
            Mockito.any()
        )).thenReturn(lockHandle);
        Mockito.when(apiKeyService.getById(apiKeyId)).thenReturn(new SysApiKey());
        AtomicLong resourceId = new AtomicLong(800);
        Mockito.when(resourceService.getOne(
            Mockito.any(com.mybatisflex.core.query.QueryWrapper.class)))
            .thenAnswer(invocation -> {
            SysApiKeyResource resource = new SysApiKeyResource();
            resource.setId(BigInteger.valueOf(resourceId.incrementAndGet()));
            return resource;
        });

        KnowledgeSharePermissionServiceImpl service =
            new KnowledgeSharePermissionServiceImpl();
        setField(service, "sysApiKeyService", apiKeyService);
        setField(service, "resourceService", resourceService);
        setField(service, "mappingService", mappingService);
        setField(service, "redisLockExecutor", redisLockExecutor);

        service.replaceApiPermissions(apiKeyId, true, false, false);
        service.replaceApiPermissions(apiKeyId, false, true, false);
        service.replaceApiPermissions(apiKeyId, false, false, true);

        @SuppressWarnings("unchecked")
        ArgumentCaptor<List<SysApiKeyResourceMapping>> mappings =
            ArgumentCaptor.forClass(List.class);
        Mockito.verify(mappingService, Mockito.times(3)).remove(
            Mockito.any(com.mybatisflex.core.query.QueryWrapper.class)
        );
        Mockito.verify(mappingService, Mockito.times(3))
            .saveBatch(mappings.capture());
        Mockito.verify(lockHandle, Mockito.times(3)).release();
        Assert.assertEquals(3, mappings.getAllValues().size());
        assertScopeMappings(
            mappings.getAllValues().get(0),
            KnowledgeApiPermissionScope.KNOWLEDGE_READ,
            8
        );
        assertScopeMappings(
            mappings.getAllValues().get(1),
            KnowledgeApiPermissionScope.KNOWLEDGE_IMPORT,
            14
        );
        assertScopeMappings(
            mappings.getAllValues().get(2),
            KnowledgeApiPermissionScope.KNOWLEDGE_MAINTENANCE,
            6
        );
    }

    /**
     * 断言一组全局知识库接口映射只包含指定权限。
     *
     * @param mappings 接口映射
     * @param expectedScope 预期权限
     * @param expectedSize 预期接口数量
     */
    private void assertScopeMappings(
        List<SysApiKeyResourceMapping> mappings,
        KnowledgeApiPermissionScope expectedScope,
        int expectedSize) {
        Assert.assertEquals(expectedSize, mappings.size());
        for (SysApiKeyResourceMapping mapping : mappings) {
            Assert.assertEquals(
                expectedScope.name(),
                mapping.getActionScope()
            );
            Assert.assertNull(mapping.getResourceTargetId());
        }
    }

    /**
     * 反射注入测试依赖。
     *
     * @param target 目标对象
     * @param fieldName 字段名
     * @param value 字段值
     * @throws Exception 字段不存在或不可访问
     */
    private static void setField(
        Object target,
        String fieldName,
        Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(target, value);
    }
}
