package tech.easyflow.chatlog.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;
import tech.easyflow.chatlog.cache.ChatHotStateService;
import tech.easyflow.chatlog.config.ChatCacheProperties;
import tech.easyflow.chatlog.domain.dto.ChatHistoryPage;
import tech.easyflow.chatlog.domain.dto.ChatMessageRecord;
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.repository.mysql.MySqlChatLogRepository;
import tech.easyflow.chatlog.repository.mysql.MySqlChatLogTableManager;
import tech.easyflow.chatlog.repository.mysql.MySqlChatSessionRepository;
import tech.easyflow.chatlog.support.ChatJsonSupport;

import java.math.BigInteger;
import java.time.YearMonth;
import java.util.ArrayList;
import java.util.List;

/**
 * {@link ChatSessionQueryServiceImpl} 单元测试。
 */
public class ChatSessionQueryServiceImplTest {

    /**
     * 会话列表必须以 MySQL 会话表为唯一权威来源，不再使用 Redis 列表索引。
     */
    @Test
    public void pageSessionsShouldUseMysqlRepositoryAsAuthority() {
        FakeSessionRepository sessionRepository = new FakeSessionRepository();
        sessionRepository.sessions = List.of(session(BigInteger.valueOf(1001), 4));
        sessionRepository.count = 1;
        ChatSessionQueryServiceImpl service = new ChatSessionQueryServiceImpl(
                sessionRepository,
                new FakeLogRepository(),
                new FakeTableManager(List.of()),
                new FakeHotStateService()
        );

        ChatSessionPage page = service.pageSessions(BigInteger.valueOf(7), null, new ChatPageQuery());

        Assert.assertEquals(1, page.getTotal());
        Assert.assertEquals(1, page.getRecords().size());
        Assert.assertEquals(1, sessionRepository.countSessionsCalls);
        Assert.assertEquals(1, sessionRepository.listSessionsCalls);
    }

    /**
     * Agent 会话查询必须把 assistantCode 过滤条件下推到 MySQL 会话表，避免混入 Bot 会话。
     */
    @Test
    public void pageSessionsShouldPassAssistantCodeFilterToMysqlRepository() {
        FakeSessionRepository sessionRepository = new FakeSessionRepository();
        sessionRepository.sessions = List.of(session(BigInteger.valueOf(1002), 2));
        sessionRepository.count = 1;
        ChatSessionQueryServiceImpl service = new ChatSessionQueryServiceImpl(
                sessionRepository,
                new FakeLogRepository(),
                new FakeTableManager(List.of()),
                new FakeHotStateService()
        );

        service.pageSessions(BigInteger.valueOf(7), BigInteger.valueOf(88), "AGENT", new ChatPageQuery());

        Assert.assertEquals("AGENT", sessionRepository.capturedListAssistantCode);
        Assert.assertEquals("AGENT", sessionRepository.capturedCountAssistantCode);
    }

    /**
     * 工作台消息分页必须走 MySQL 热表主线查询，并保持分页参数语义。
     */
    @Test
    public void pageMainlineMessagesShouldReadMysqlHotTables() {
        FakeSessionRepository sessionRepository = new FakeSessionRepository();
        sessionRepository.summary = session(BigInteger.valueOf(2001), 6);
        FakeLogRepository logRepository = new FakeLogRepository();
        logRepository.records = List.of(message(3001), message(3002));
        List<YearMonth> months = List.of(YearMonth.of(2026, 5));
        ChatSessionQueryServiceImpl service = new ChatSessionQueryServiceImpl(
                sessionRepository,
                logRepository,
                new FakeTableManager(months),
                new FakeHotStateService()
        );
        ChatPageQuery query = new ChatPageQuery();
        query.setPageNumber(2);
        query.setPageSize(2);

        ChatHistoryPage page = service.pageMainlineMessages(BigInteger.valueOf(2001), query);

        Assert.assertEquals(6, page.getTotal());
        Assert.assertEquals(2, page.getRecords().size());
        Assert.assertEquals(BigInteger.valueOf(2001), logRepository.capturedSessionId);
        Assert.assertEquals(months, logRepository.capturedMonths);
        Assert.assertEquals(2, logRepository.capturedOffset);
        Assert.assertEquals(2, logRepository.capturedLimit);
    }

    /**
     * 当 MySQL 摘要计数滞后时，分页 total 至少覆盖当前已返回的数据范围。
     */
    @Test
    public void pageMainlineMessagesShouldNotReturnTotalSmallerThanCurrentPage() {
        FakeSessionRepository sessionRepository = new FakeSessionRepository();
        sessionRepository.summary = session(BigInteger.valueOf(2002), 1);
        FakeLogRepository logRepository = new FakeLogRepository();
        logRepository.records = List.of(message(4001), message(4002));
        ChatSessionQueryServiceImpl service = new ChatSessionQueryServiceImpl(
                sessionRepository,
                logRepository,
                new FakeTableManager(List.of(YearMonth.of(2026, 5))),
                new FakeHotStateService()
        );
        ChatPageQuery query = new ChatPageQuery();
        query.setPageNumber(2);
        query.setPageSize(2);

        ChatHistoryPage page = service.pageMainlineMessages(BigInteger.valueOf(2002), query);

        Assert.assertEquals(4, page.getTotal());
    }

    private static ChatSessionSummary session(BigInteger id, int messageCount) {
        ChatSessionSummary summary = new ChatSessionSummary();
        summary.setId(id);
        summary.setUserId(BigInteger.valueOf(7));
        summary.setMessageCount(messageCount);
        return summary;
    }

    private static ChatMessageRecord message(long id) {
        ChatMessageRecord record = new ChatMessageRecord();
        record.setId(BigInteger.valueOf(id));
        return record;
    }

    /**
     * MySQL 会话仓储测试替身。
     */
    private static final class FakeSessionRepository extends MySqlChatSessionRepository {

        private long count;
        private int countSessionsCalls;
        private int listSessionsCalls;
        private String capturedListAssistantCode;
        private String capturedCountAssistantCode;
        private ChatSessionSummary summary;
        private List<ChatSessionSummary> sessions = new ArrayList<>();

        private FakeSessionRepository() {
            super(null, null);
        }

        @Override
        public List<ChatSessionSummary> listSessions(BigInteger userId, BigInteger assistantId, ChatPageQuery query) {
            return listSessions(userId, assistantId, null, query);
        }

        @Override
        public List<ChatSessionSummary> listSessions(BigInteger userId, BigInteger assistantId, String assistantCode, ChatPageQuery query) {
            listSessionsCalls += 1;
            capturedListAssistantCode = assistantCode;
            return sessions;
        }

        @Override
        public long countSessions(BigInteger userId, BigInteger assistantId) {
            return countSessions(userId, assistantId, null);
        }

        @Override
        public long countSessions(BigInteger userId, BigInteger assistantId, String assistantCode) {
            countSessionsCalls += 1;
            capturedCountAssistantCode = assistantCode;
            return count;
        }

        @Override
        public ChatSessionSummary findBySessionId(BigInteger sessionId) {
            return summary;
        }
    }

    /**
     * MySQL 消息仓储测试替身。
     */
    private static final class FakeLogRepository extends MySqlChatLogRepository {

        private BigInteger capturedSessionId;
        private List<YearMonth> capturedMonths;
        private long capturedOffset;
        private int capturedLimit;
        private List<ChatMessageRecord> records = new ArrayList<>();

        private FakeLogRepository() {
            super(null, null, new ChatJsonSupport(new ObjectMapper()));
        }

        @Override
        public List<ChatMessageRecord> listMainlineMessages(BigInteger sessionId, List<YearMonth> months, long offset, int limit) {
            capturedSessionId = sessionId;
            capturedMonths = months;
            capturedOffset = offset;
            capturedLimit = limit;
            return records;
        }
    }

    /**
     * MySQL 热表管理器测试替身。
     */
    private static final class FakeTableManager extends MySqlChatLogTableManager {

        private final List<YearMonth> months;

        private FakeTableManager(List<YearMonth> months) {
            super(null, null);
            this.months = months;
        }

        @Override
        public List<YearMonth> listRecentExistingMonths(int retentionMonths) {
            return months;
        }
    }

    /**
     * Redis 热态测试替身，避免单测依赖真实 Redis。
     */
    private static final class FakeHotStateService extends ChatHotStateService {

        private FakeHotStateService() {
            super(null, new ObjectMapper(), new ChatCacheProperties());
        }

        @Override
        public ChatSessionSummary getSessionSummary(BigInteger sessionId) {
            return null;
        }

        @Override
        public void cacheSessionSummary(ChatSessionSummary summary) {
        }

        @Override
        public List<ChatMessageRecord> getSessionTail(BigInteger sessionId) {
            return null;
        }

        @Override
        public void setSessionTail(BigInteger sessionId, List<ChatMessageRecord> records) {
        }
    }
}
