package tech.easyflow.ai.documentimport.task;

import org.junit.Assert;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import tech.easyflow.ai.entity.Document;
import tech.easyflow.ai.mapper.DocumentMapper;

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

/**
 * {@link DocumentImportTaskStatusStreamService} 回归测试。
 */
public class DocumentImportTaskStatusStreamServiceTest {

    /**
     * 验证文档状态变更会向 Redis 广播文档 ID。
     *
     * @throws Exception 反射注入异常
     */
    @Test
    public void publishAfterCommitShouldBroadcastDocumentId() throws Exception {
        StringRedisTemplate redisTemplate = Mockito.mock(StringRedisTemplate.class);
        DocumentImportTaskStatusStreamService service = new DocumentImportTaskStatusStreamService();
        setField(service, "documentMapper", mockDocumentMapper());
        setField(service, "sseThreadPool", directExecutor());
        setField(service, "stringRedisTemplate", redisTemplate);
        setField(service, "statusBroadcastProperties", statusBroadcastProperties());

        service.publishAfterCommit(BigInteger.valueOf(101));

        Mockito.verify(redisTemplate).convertAndSend("easyflow:document-import:test-status", "101");
    }

    /**
     * 验证收到 Redis 广播后会重新查询文档状态。
     *
     * @throws Exception 反射注入异常
     */
    @Test
    public void publishLocalShouldReloadDocumentStatus() throws Exception {
        AtomicReference<BigInteger> selectedIdRef = new AtomicReference<BigInteger>();
        DocumentImportTaskStatusStreamService service = new DocumentImportTaskStatusStreamService();
        setField(service, "documentMapper", mockDocumentMapper(selectedIdRef));
        setField(service, "sseThreadPool", directExecutor());
        setField(service, "stringRedisTemplate", Mockito.mock(StringRedisTemplate.class));
        setField(service, "statusBroadcastProperties", statusBroadcastProperties());

        service.publishLocal(BigInteger.valueOf(202));

        Assert.assertEquals(BigInteger.valueOf(202), selectedIdRef.get());
    }

    private DocumentImportStatusBroadcastProperties statusBroadcastProperties() {
        DocumentImportStatusBroadcastProperties properties = new DocumentImportStatusBroadcastProperties();
        properties.setStatusBroadcastChannel("easyflow:document-import:test-status");
        return properties;
    }

    private DocumentMapper mockDocumentMapper() {
        return mockDocumentMapper(new AtomicReference<BigInteger>());
    }

    private DocumentMapper mockDocumentMapper(AtomicReference<BigInteger> selectedIdRef) {
        DocumentMapper mapper = Mockito.mock(DocumentMapper.class);
        Mockito.when(mapper.selectOneById(ArgumentMatchers.any())).thenAnswer(invocation -> {
            Object id = invocation.getArgument(0);
            selectedIdRef.set((BigInteger) id);
            Document document = new Document();
            document.setId((BigInteger) id);
            document.setCollectionId(BigInteger.valueOf(1));
            return document;
        });
        return mapper;
    }

    private ThreadPoolTaskExecutor directExecutor() {
        ThreadPoolTaskExecutor executor = Mockito.mock(ThreadPoolTaskExecutor.class);
        Mockito.doAnswer(invocation -> {
            Runnable runnable = invocation.getArgument(0);
            runnable.run();
            return null;
        }).when(executor).execute(ArgumentMatchers.any(Runnable.class));
        return executor;
    }

    private void setField(Object target, String fieldName, Object value) throws Exception {
        Field field = DocumentImportTaskStatusStreamService.class.getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(target, value);
    }
}
