package tech.easyflow.chatlog.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import tech.easyflow.chatlog.domain.dto.ChatHistoryPage;
import tech.easyflow.chatlog.domain.dto.ChatSessionPage;
import tech.easyflow.chatlog.domain.dto.ChatSessionSummary;
import tech.easyflow.chatlog.domain.query.ChatPageQuery;
import tech.easyflow.chatlog.domain.query.ChatSessionFilterQuery;
import tech.easyflow.chatlog.repository.analyticaldb.ChatAnalyticalDBRepository;
import tech.easyflow.chatlog.service.ChatHistoryQueryService;
import tech.easyflow.chatlog.service.ChatRoundOperateService;
import tech.easyflow.chatlog.service.ChatSessionCommandService;
import tech.easyflow.chatlog.service.ChatSessionQueryService;
import tech.easyflow.chatlog.support.ChatJsonSupport;
import tech.easyflow.common.analyticaldb.core.AnalyticalDBOperations;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.lang.reflect.Proxy;
import java.math.BigInteger;

/**
 * {@link ChatHistoryManageServiceImpl} 管理端 Agent 会话边界测试。
 */
public class ChatHistoryManageServiceImplTest {

    private StubChatAnalyticalDBRepository chatAnalyticalDBRepository;
    private ChatHistoryManageServiceImpl service;

    /**
     * 初始化管理端聊天历史服务及其依赖。
     */
    @Before
    public void setUp() {
        ChatSessionQueryService chatSessionQueryService = unusedDependency(ChatSessionQueryService.class);
        ChatSessionCommandService chatSessionCommandService = unusedDependency(ChatSessionCommandService.class);
        ChatHistoryQueryService chatHistoryQueryService = unusedDependency(ChatHistoryQueryService.class);
        ChatRoundOperateService chatRoundOperateService = unusedDependency(ChatRoundOperateService.class);
        chatAnalyticalDBRepository = new StubChatAnalyticalDBRepository();
        service = new ChatHistoryManageServiceImpl(
                chatSessionQueryService,
                chatSessionCommandService,
                chatHistoryQueryService,
                chatRoundOperateService,
                chatAnalyticalDBRepository
        );
    }

    /**
     * 验证管理端列表始终覆盖客户端传入的会话类型为 Agent。
     */
    @Test
    public void queryAdminSessionsShouldForceAgentAssistantCode() {
        ChatSessionFilterQuery query = new ChatSessionFilterQuery();
        query.setAssistantCode("BOT");

        service.queryAdminSessions(query);

        Assert.assertEquals("AGENT", query.getAssistantCode());
        Assert.assertSame(query, chatAnalyticalDBRepository.lastPageQuery);
    }

    /**
     * 验证管理端可以读取正式 Agent 会话。
     */
    @Test
    public void getAdminSessionShouldReturnAgentSession() {
        BigInteger sessionId = BigInteger.valueOf(1001);
        ChatSessionSummary summary = session(sessionId, "AGENT", 0);
        chatAnalyticalDBRepository.sessionResult = summary;

        ChatSessionSummary result = service.getAdminSession(sessionId);

        Assert.assertSame(summary, result);
    }

    /**
     * 验证正式 Agent 会话可以继续读取历史消息。
     */
    @Test
    public void queryAdminMessagesShouldReturnAgentHistory() {
        BigInteger sessionId = BigInteger.valueOf(1004);
        ChatHistoryPage expectedPage = new ChatHistoryPage();
        chatAnalyticalDBRepository.sessionResult = session(sessionId, "AGENT", 0);
        service = new ChatHistoryManageServiceImpl(
                unusedDependency(ChatSessionQueryService.class),
                unusedDependency(ChatSessionCommandService.class),
                (requestedSessionId, query) -> expectedPage,
                unusedDependency(ChatRoundOperateService.class),
                chatAnalyticalDBRepository
        );
        ChatPageQuery query = new ChatPageQuery();
        query.setPageNumber(2);

        ChatHistoryPage result = service.queryAdminMessages(sessionId, query);

        Assert.assertSame(expectedPage, result);
    }

    /**
     * 验证管理端拒绝旧 Bot 会话详情。
     */
    @Test
    public void getAdminSessionShouldRejectBotSession() {
        BigInteger sessionId = BigInteger.valueOf(1002);
        chatAnalyticalDBRepository.sessionResult = session(sessionId, "BOT", 0);

        BusinessException exception = Assert.assertThrows(
                BusinessException.class,
                () -> service.getAdminSession(sessionId)
        );

        Assert.assertEquals("Agent 会话不存在", exception.getMessage());
    }

    /**
     * 验证旧 Bot 会话在消息查询前即被拒绝，不会继续读取历史消息。
     */
    @Test
    public void queryAdminMessagesShouldRejectBotBeforeHistoryLookup() {
        BigInteger sessionId = BigInteger.valueOf(1003);
        chatAnalyticalDBRepository.sessionResult = session(sessionId, "BOT", 0);

        Assert.assertThrows(
                BusinessException.class,
                () -> service.queryAdminMessages(sessionId, new ChatPageQuery())
        );
    }

    /**
     * 验证旧 Bot 会话在答案版本列表查询前即被拒绝。
     */
    @Test
    public void listAdminRoundVariantsShouldRejectBotBeforeRoundLookup() {
        BigInteger sessionId = BigInteger.valueOf(1005);
        chatAnalyticalDBRepository.sessionResult = session(sessionId, "BOT", 0);

        Assert.assertThrows(
                BusinessException.class,
                () -> service.listAdminRoundVariants(sessionId, BigInteger.ONE)
        );
    }

    /**
     * 验证旧 Bot 会话在答案版本选择前即被拒绝。
     */
    @Test
    public void selectAdminRoundVariantShouldRejectBotBeforeRoundUpdate() {
        BigInteger sessionId = BigInteger.valueOf(1006);
        chatAnalyticalDBRepository.sessionResult = session(sessionId, "BOT", 0);

        Assert.assertThrows(
                BusinessException.class,
                () -> service.selectAdminRoundVariant(
                        sessionId,
                        BigInteger.ONE,
                        1,
                        BigInteger.TEN
                )
        );
    }

    /**
     * 创建不应在当前测试路径中被调用的接口依赖。
     *
     * @param dependencyType 依赖接口类型
     * @param <T> 依赖接口类型
     * @return 调用任意方法即失败的代理对象
     */
    private <T> T unusedDependency(Class<T> dependencyType) {
        Object proxy = Proxy.newProxyInstance(
                dependencyType.getClassLoader(),
                new Class<?>[]{dependencyType},
                (instance, method, args) -> {
                    throw new AssertionError("测试路径不应调用依赖方法: " + method.getName());
                }
        );
        return dependencyType.cast(proxy);
    }

    /**
     * 构造会话摘要。
     *
     * @param sessionId 会话 ID
     * @param assistantCode 助手类型编码
     * @param isDeleted 删除标识
     * @return 会话摘要
     */
    private ChatSessionSummary session(BigInteger sessionId, String assistantCode, int isDeleted) {
        ChatSessionSummary summary = new ChatSessionSummary();
        summary.setId(sessionId);
        summary.setAssistantCode(assistantCode);
        summary.setIsDeleted(isDeleted);
        return summary;
    }

    /**
     * 仅记录管理端会话查询参数和返回值的分析库仓储桩。
     */
    private static class StubChatAnalyticalDBRepository extends ChatAnalyticalDBRepository {

        private ChatSessionFilterQuery lastPageQuery;
        private ChatSessionSummary sessionResult;

        /**
         * 创建不连接真实分析库的仓储桩。
         */
        private StubChatAnalyticalDBRepository() {
            super(
                    new StaticListableBeanFactory().getBeanProvider(AnalyticalDBOperations.class),
                    null,
                    new ChatJsonSupport(new ObjectMapper())
            );
        }

        /**
         * 记录分页筛选参数。
         *
         * @param query 会话筛选条件
         * @return 空分页结果
         */
        @Override
        public ChatSessionPage pageSessions(ChatSessionFilterQuery query) {
            lastPageQuery = query;
            return new ChatSessionPage();
        }

        /**
         * 返回预设的会话摘要。
         *
         * @param sessionId 会话 ID
         * @return 预设会话摘要
         */
        @Override
        public ChatSessionSummary getSession(BigInteger sessionId) {
            return sessionResult;
        }
    }
}
