package tech.easyflow.chatlog.service.impl;

import org.springframework.stereotype.Service;
import tech.easyflow.chatlog.cache.ChatHotStateService;
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.service.ChatSessionQueryService;

import java.math.BigInteger;
import java.util.*;

@Service
public class ChatSessionQueryServiceImpl implements ChatSessionQueryService {

    private final MySqlChatSessionRepository sessionRepository;
    private final MySqlChatLogRepository logRepository;
    private final MySqlChatLogTableManager tableManager;
    private final ChatHotStateService chatHotStateService;

    public ChatSessionQueryServiceImpl(MySqlChatSessionRepository sessionRepository,
                                       MySqlChatLogRepository logRepository,
                                       MySqlChatLogTableManager tableManager,
                                       ChatHotStateService chatHotStateService) {
        this.sessionRepository = sessionRepository;
        this.logRepository = logRepository;
        this.tableManager = tableManager;
        this.chatHotStateService = chatHotStateService;
    }

    @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) {
        return sessionRepository.listSessions(userId, assistantId, assistantCode, query);
    }

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

    @Override
    public long countSessions(BigInteger userId, BigInteger assistantId, String assistantCode) {
        return sessionRepository.countSessions(userId, assistantId, assistantCode);
    }

    @Override
    public ChatSessionPage pageSessions(BigInteger userId, BigInteger assistantId, ChatPageQuery query) {
        return pageSessions(userId, assistantId, null, query);
    }

    @Override
    public ChatSessionPage pageSessions(BigInteger userId, BigInteger assistantId, String assistantCode, ChatPageQuery query) {
        ChatSessionPage page = new ChatSessionPage();
        page.setPageNumber(query.getPageNumber());
        page.setPageSize(query.getPageSize());

        page.setTotal(sessionRepository.countSessions(userId, assistantId, assistantCode));
        page.setRecords(listSessions(userId, assistantId, assistantCode, query));
        return page;
    }

    @Override
    public ChatSessionSummary getSessionSummary(BigInteger sessionId) {
        ChatSessionSummary cached = chatHotStateService.getSessionSummary(sessionId);
        if (cached != null) {
            return cached;
        }
        ChatSessionSummary summary = sessionRepository.findBySessionId(sessionId);
        if (summary != null) {
            chatHotStateService.cacheSessionSummary(summary);
        }
        return summary;
    }

    @Override
    public ChatHistoryPage pageMainlineMessages(BigInteger sessionId, ChatPageQuery query) {
        ChatHistoryPage page = new ChatHistoryPage();
        page.setPageNumber(query.getPageNumber());
        page.setPageSize(query.getPageSize());
        ChatSessionSummary summary = getSessionSummary(sessionId);
        long total = summary == null || summary.getMessageCount() == null ? 0L : summary.getMessageCount();
        List<ChatMessageRecord> records = logRepository.listMainlineMessages(
                sessionId,
                tableManager.listRecentExistingMonths(3),
                query.getOffset(),
                Math.toIntExact(query.getPageSize())
        );
        page.setRecords(records);
        page.setTotal(Math.max(total, query.getOffset() + records.size()));
        return page;
    }

    @Override
    public List<ChatMessageRecord> listMainlineMessages(BigInteger sessionId) {
        return logRepository.listMainlineMessages(sessionId, tableManager.listRecentExistingMonths(3));
    }

    @Override
    public List<ChatMessageRecord> getRecentTail(BigInteger sessionId, int limit) {
        List<ChatMessageRecord> cached = chatHotStateService.getSessionTail(sessionId);
        if (cached != null && isTailReliable(cached)) {
            return cached.subList(0, Math.min(limit, cached.size()));
        }
        List<ChatMessageRecord> records = logRepository.listRecentTail(sessionId, tableManager.listRecentExistingMonths(3), limit);
        chatHotStateService.setSessionTail(sessionId, records);
        return records;
    }

    /**
     * 校验 Redis tail 是否符合当前主线版本语义，防止过期选中版本把可见回答过滤掉。
     *
     * @param records Redis tail 消息
     * @return true 表示可直接使用缓存
     */
    private boolean isTailReliable(List<ChatMessageRecord> records) {
        Map<BigInteger, Integer> selectedVariantByRound = new LinkedHashMap<>();
        Map<BigInteger, Set<Integer>> assistantVariantsByRound = new LinkedHashMap<>();
        for (ChatMessageRecord record : records) {
            if (record == null || record.getRoundId() == null) {
                continue;
            }
            Integer selectedVariantIndex = record.getSelectedVariantIndex();
            if (selectedVariantIndex != null && selectedVariantIndex > 0) {
                Integer previous = selectedVariantByRound.putIfAbsent(record.getRoundId(), selectedVariantIndex);
                if (previous != null && !Objects.equals(previous, selectedVariantIndex)) {
                    return false;
                }
            }
            if ("assistant".equalsIgnoreCase(record.getSenderRole())
                    && record.getVariantIndex() != null
                    && record.getVariantIndex() > 0) {
                assistantVariantsByRound
                        .computeIfAbsent(record.getRoundId(), key -> new LinkedHashSet<>())
                        .add(record.getVariantIndex());
            }
        }
        for (Map.Entry<BigInteger, Integer> entry : selectedVariantByRound.entrySet()) {
            Set<Integer> visibleVariants = assistantVariantsByRound.get(entry.getKey());
            if (visibleVariants != null && !visibleVariants.isEmpty() && !visibleVariants.contains(entry.getValue())) {
                return false;
            }
        }
        return true;
    }
}
