package tech.easyflow.admin.service.ai;

import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import tech.easyflow.admin.dto.chatworkspace.ChatWorkspaceAssistantView;
import tech.easyflow.admin.dto.chatworkspace.ChatWorkspaceConversationView;
import tech.easyflow.admin.dto.chatworkspace.ChatWorkspaceKnowledgeView;
import tech.easyflow.admin.dto.chatworkspace.ChatWorkspaceReadOnlyReason;
import tech.easyflow.admin.dto.chatworkspace.ChatWorkspaceSessionDetailView;
import tech.easyflow.admin.dto.chatworkspace.ChatWorkspaceSessionPage;
import tech.easyflow.admin.dto.chatworkspace.ChatWorkspaceSessionView;
import tech.easyflow.ai.entity.Bot;
import tech.easyflow.ai.entity.DocumentCollection;
import tech.easyflow.ai.enums.PublishStatus;
import tech.easyflow.ai.permission.KnowledgeReadAccessSnapshot;
import tech.easyflow.ai.permission.KnowledgeVisibilityQueryHelper;
import tech.easyflow.ai.service.BotService;
import tech.easyflow.ai.service.DocumentCollectionService;
import tech.easyflow.chatlog.domain.command.ChatSessionUpsertCommand;
import tech.easyflow.chatlog.domain.dto.ChatHistoryPage;
import tech.easyflow.chatlog.domain.dto.ChatMessageRecord;
import tech.easyflow.chatlog.domain.dto.ChatSessionExtPayload;
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.service.ChatRoundOperateService;
import tech.easyflow.chatlog.service.ChatSessionCommandService;
import tech.easyflow.chatlog.service.ChatSessionQueryService;
import tech.easyflow.chatlog.support.ChatJsonSupport;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.entity.vo.RoleCategoryAccessSnapshot;
import tech.easyflow.system.service.CategoryPermissionService;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import static tech.easyflow.ai.entity.table.BotTableDef.BOT;

/**
 * 管理端聊天工作台服务。
 */
@Service
public class ChatWorkspaceService {

    private final ChatSessionQueryService chatSessionQueryService;
    private final ChatSessionCommandService chatSessionCommandService;
    private final ChatRoundOperateService chatRoundOperateService;
    private final BotService botService;
    private final DocumentCollectionService documentCollectionService;
    private final CategoryPermissionService categoryPermissionService;
    private final KnowledgeVisibilityQueryHelper knowledgeVisibilityQueryHelper;
    private final ChatJsonSupport chatJsonSupport;

    public ChatWorkspaceService(ChatSessionQueryService chatSessionQueryService,
                                ChatSessionCommandService chatSessionCommandService,
                                ChatRoundOperateService chatRoundOperateService,
                                BotService botService,
                                DocumentCollectionService documentCollectionService,
                                CategoryPermissionService categoryPermissionService,
                                KnowledgeVisibilityQueryHelper knowledgeVisibilityQueryHelper,
                                ChatJsonSupport chatJsonSupport) {
        this.chatSessionQueryService = chatSessionQueryService;
        this.chatSessionCommandService = chatSessionCommandService;
        this.chatRoundOperateService = chatRoundOperateService;
        this.botService = botService;
        this.documentCollectionService = documentCollectionService;
        this.categoryPermissionService = categoryPermissionService;
        this.knowledgeVisibilityQueryHelper = knowledgeVisibilityQueryHelper;
        this.chatJsonSupport = chatJsonSupport;
    }

    /**
     * 查询当前用户会话分页。
     *
     * @param account 当前登录用户
     * @param assistantId 助手过滤条件
     * @param query 分页参数
     * @return 工作台会话分页
     */
    public ChatWorkspaceSessionPage queryCurrentUserSessions(LoginAccount account, BigInteger assistantId, ChatPageQuery query) {
        ChatSessionPage page = chatSessionQueryService.pageSessions(account.getId(), assistantId, query);
        Map<BigInteger, AssistantAvailability> availabilityMap = resolveAssistantAvailability(account, page.getRecords());
        ChatWorkspaceSessionPage result = new ChatWorkspaceSessionPage();
        result.setTotal(page.getTotal());
        result.setPageNumber(page.getPageNumber());
        result.setPageSize(page.getPageSize());
        List<ChatWorkspaceSessionView> records = new ArrayList<>();
        for (ChatSessionSummary summary : page.getRecords()) {
            records.add(toSessionView(summary, availabilityMap.get(summary.getAssistantId())));
        }
        result.setRecords(records);
        return result;
    }

    /**
     * 查询当前用户会话详情。
     *
     * @param account 当前登录用户
     * @param sessionId 会话 ID
     * @return 工作台会话详情
     */
    public ChatWorkspaceSessionDetailView getCurrentUserSession(LoginAccount account, BigInteger sessionId) {
        ChatSessionSummary summary = requireUserSession(account, sessionId);
        AssistantAvailability availability = resolveAssistantAvailability(account, List.of(summary)).get(summary.getAssistantId());
        ChatWorkspaceSessionDetailView detail = new ChatWorkspaceSessionDetailView();
        fillSessionView(detail, summary, availability);
        if (availability != null && availability.displayBot() != null) {
            detail.setAssistant(toAssistantView(availability.displayBot(), summary));
            detail.setBoundKnowledges(resolveBoundKnowledges(availability.displayBot()));
        } else {
            detail.setAssistant(toAssistantView(null, summary));
        }
        ExtraKnowledgeResolution extraKnowledgeResolution = resolveExtraKnowledges(summary);
        detail.setExtraKnowledges(extraKnowledgeResolution.validKnowledges());
        detail.setRemovedExtraKnowledgeNames(extraKnowledgeResolution.removedNames());
        if (extraKnowledgeResolution.shouldSync()) {
            syncSessionExtraKnowledges(summary, extraKnowledgeResolution.validKnowledgeIds(), account.getId());
        }
        return detail;
    }

    /**
     * 查询当前用户会话消息。
     *
     * @param account 当前登录用户
     * @param sessionId 会话 ID
     * @param query 分页参数
     * @return 历史消息分页
     */
    public ChatHistoryPage queryCurrentUserMessages(LoginAccount account, BigInteger sessionId, ChatPageQuery query) {
        ChatSessionSummary summary = requireUserSession(account, sessionId);
        ChatHistoryPage firstPage = restoreRecentMessages(summary, query);
        if (firstPage != null) {
            return firstPage;
        }
        return chatSessionQueryService.pageMainlineMessages(sessionId, query);
    }

    /**
     * 查询当前用户完整工作台会话。
     *
     * @param account 当前登录用户
     * @param sessionId 会话 ID
     * @return 完整会话视图
     */
    public ChatWorkspaceConversationView getCurrentUserConversation(LoginAccount account, BigInteger sessionId) {
        requireUserSession(account, sessionId);
        List<ChatMessageRecord> records = chatSessionQueryService.listMainlineMessages(sessionId);
        Map<String, List<ChatMessageRecord>> variantsByRound = new LinkedHashMap<>();
        Set<BigInteger> roundIds = new LinkedHashSet<>();
        for (ChatMessageRecord record : records) {
            if (record == null || record.getRoundId() == null) {
                continue;
            }
            Integer variantCount = record.getVariantCount();
            if (variantCount != null && variantCount > 1) {
                roundIds.add(record.getRoundId());
            }
        }
        for (BigInteger roundId : roundIds) {
            variantsByRound.put(roundId.toString(), chatRoundOperateService.listVariants(sessionId, roundId));
        }
        ChatWorkspaceConversationView view = new ChatWorkspaceConversationView();
        view.setRecords(records);
        view.setVariantsByRound(variantsByRound);
        view.setTotal(records.size());
        return view;
    }

    /**
     * 重命名当前用户会话。
     *
     * @param account 当前登录用户
     * @param sessionId 会话 ID
     * @param title 新标题
     */
    public void renameCurrentUserSession(LoginAccount account, BigInteger sessionId, String title) {
        if (!StringUtils.hasText(title)) {
            throw new BusinessException("标题不能为空");
        }
        requireUserSession(account, sessionId);
        chatSessionCommandService.renameSession(sessionId, account.getId(), title.trim(), account.getId());
    }

    /**
     * 删除当前用户会话。
     *
     * @param account 当前登录用户
     * @param sessionId 会话 ID
     */
    public void deleteCurrentUserSession(LoginAccount account, BigInteger sessionId) {
        requireUserSession(account, sessionId);
        chatSessionCommandService.deleteSession(sessionId, account.getId(), account.getId());
    }

    public List<ChatMessageRecord> listCurrentUserRoundVariants(LoginAccount account, BigInteger sessionId, BigInteger roundId) {
        requireUserSession(account, sessionId);
        return chatRoundOperateService.listVariants(sessionId, roundId);
    }

    public ChatMessageRecord selectCurrentUserRoundVariant(LoginAccount account, BigInteger sessionId, BigInteger roundId, Integer variantIndex) {
        requireUserSession(account, sessionId);
        return chatRoundOperateService.selectVariant(sessionId, roundId, variantIndex, account.getId());
    }

    /**
     * 发送前校验会话是否仍可继续聊天。
     *
     * @param account 当前登录用户
     * @param sessionId 会话 ID
     * @param requestBotId 本次请求助手 ID
     */
    public void assertSessionContinuable(LoginAccount account, BigInteger sessionId, BigInteger requestBotId) {
        ChatSessionSummary summary = chatSessionQueryService.getSessionSummary(sessionId);
        if (summary == null || Integer.valueOf(1).equals(summary.getIsDeleted())) {
            return;
        }
        if (!Objects.equals(summary.getUserId(), account.getId())) {
            throw new BusinessException("无权访问该会话");
        }
        if (requestBotId != null && summary.getAssistantId() != null && !Objects.equals(summary.getAssistantId(), requestBotId)) {
            throw new BusinessException("当前会话与所选聊天助手不匹配");
        }
        AssistantAvailability availability = resolveAssistantAvailability(account, List.of(summary)).get(summary.getAssistantId());
        if (availability == null || !availability.continuable()) {
            throw new BusinessException(buildReadOnlyMessage(availability == null ? ChatWorkspaceReadOnlyReason.ASSISTANT_DELETED : availability.reason()));
        }
    }

    private ChatSessionSummary requireUserSession(LoginAccount account, BigInteger sessionId) {
        ChatSessionSummary summary = chatSessionQueryService.getSessionSummary(sessionId);
        if (summary == null || Integer.valueOf(1).equals(summary.getIsDeleted())) {
            throw new BusinessException("会话不存在");
        }
        if (!Objects.equals(summary.getUserId(), account.getId())) {
            throw new BusinessException("无权访问该会话");
        }
        return summary;
    }

    /**
     * 首屏优先从热态恢复最近消息，避免分析库延迟导致刚完成的回复不可见。
     *
     * @param summary 会话摘要
     * @param query 分页参数
     * @return 命中热态时返回恢复结果，否则返回 null 继续走历史库
     */
    private ChatHistoryPage restoreRecentMessages(ChatSessionSummary summary, ChatPageQuery query) {
        if (summary == null || query == null || query.getPageNumber() != 1) {
            return null;
        }
        List<tech.easyflow.chatlog.domain.dto.ChatMessageRecord> records =
                chatSessionQueryService.getRecentTail(summary.getId(), Math.toIntExact(query.getPageSize()));
        if (records == null || records.isEmpty()) {
            return null;
        }
        if (!isRestoredTailReliable(records)) {
            return null;
        }
        ChatHistoryPage page = new ChatHistoryPage();
        page.setPageNumber(query.getPageNumber());
        page.setPageSize(query.getPageSize());
        page.setRecords(records);
        long total = summary.getMessageCount() == null ? 0L : summary.getMessageCount();
        page.setTotal(Math.max(total, records.size()));
        return page;
    }

    /**
     * 校验 Redis tail 是否仍符合当前主线版本语义。
     *
     * @param records Redis tail 消息
     * @return true 表示可直接用于首屏恢复
     */
    private boolean isRestoredTailReliable(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;
    }

    private Map<BigInteger, AssistantAvailability> resolveAssistantAvailability(LoginAccount account, List<ChatSessionSummary> sessions) {
        Map<BigInteger, AssistantAvailability> result = new LinkedHashMap<>();
        if (sessions == null || sessions.isEmpty()) {
            return result;
        }
        Set<BigInteger> assistantIds = new LinkedHashSet<>();
        for (ChatSessionSummary session : sessions) {
            if (session != null && session.getAssistantId() != null) {
                assistantIds.add(session.getAssistantId());
            }
        }
        if (assistantIds.isEmpty()) {
            return result;
        }
        List<Bot> bots = botService.list(QueryWrapper.create().where(BOT.ID.in(assistantIds)));
        Map<BigInteger, Bot> botMap = new LinkedHashMap<>();
        for (Bot bot : bots) {
            botMap.put(bot.getId(), bot);
        }
        RoleCategoryAccessSnapshot accessSnapshot = categoryPermissionService.getAccess("BOT", account);
        for (BigInteger assistantId : assistantIds) {
            Bot currentBot = botMap.get(assistantId);
            if (currentBot == null) {
                result.put(assistantId, new AssistantAvailability(false, ChatWorkspaceReadOnlyReason.ASSISTANT_DELETED, null));
                continue;
            }
            if (!accessSnapshot.canAccess(currentBot.getCreatedBy(), currentBot.getCategoryId())) {
                result.put(assistantId, new AssistantAvailability(false, ChatWorkspaceReadOnlyReason.NO_PERMISSION, null));
                continue;
            }
            Bot displayBot = botService.toPublishedView(currentBot);
            boolean online = Integer.valueOf(1).equals(currentBot.getStatus())
                    && PublishStatus.from(currentBot.getPublishStatus()) == PublishStatus.PUBLISHED;
            result.put(assistantId, new AssistantAvailability(
                    online,
                    online ? null : ChatWorkspaceReadOnlyReason.ASSISTANT_OFFLINE,
                    displayBot
            ));
        }
        return result;
    }

    private ChatWorkspaceSessionView toSessionView(ChatSessionSummary summary, AssistantAvailability availability) {
        ChatWorkspaceSessionView view = new ChatWorkspaceSessionView();
        fillSessionView(view, summary, availability);
        return view;
    }

    private void fillSessionView(ChatWorkspaceSessionView view, ChatSessionSummary summary, AssistantAvailability availability) {
        view.setSessionId(summary.getId());
        view.setAssistantId(summary.getAssistantId());
        view.setAssistantCode(summary.getAssistantCode());
        view.setAssistantName(summary.getAssistantName());
        view.setTitle(summary.getTitle());
        view.setLastMessagePreview(summary.getLastMessagePreview());
        view.setMessageCount(summary.getMessageCount());
        view.setAccessAt(summary.getAccessAt());
        view.setLastMessageAt(summary.getLastMessageAt());
        view.setContinuable(availability != null && availability.continuable());
        view.setReadOnlyReason(availability == null ? ChatWorkspaceReadOnlyReason.ASSISTANT_DELETED : availability.reason());
    }

    private ChatWorkspaceAssistantView toAssistantView(Bot bot, ChatSessionSummary summary) {
        ChatWorkspaceAssistantView view = new ChatWorkspaceAssistantView();
        if (bot != null) {
            view.setId(bot.getId());
            view.setAlias(bot.getAlias());
            view.setTitle(bot.getTitle());
            view.setDescription(bot.getDescription());
            view.setIcon(bot.getIcon());
            return view;
        }
        view.setId(summary == null ? null : summary.getAssistantId());
        view.setAlias(summary == null ? null : summary.getAssistantCode());
        view.setTitle(summary == null ? null : summary.getAssistantName());
        return view;
    }

    private List<ChatWorkspaceKnowledgeView> resolveBoundKnowledges(Bot displayBot) {
        if (displayBot == null || displayBot.getPublishedSnapshotJson() == null) {
            return List.of();
        }
        Object rawBindings = displayBot.getPublishedSnapshotJson().get("knowledgeBindings");
        if (!(rawBindings instanceof List<?> bindings) || bindings.isEmpty()) {
            return List.of();
        }
        List<BigInteger> knowledgeIds = new ArrayList<>();
        for (Object binding : bindings) {
            if (!(binding instanceof Map<?, ?> bindingMap) || bindingMap.get("knowledgeId") == null) {
                continue;
            }
            knowledgeIds.add(new BigInteger(String.valueOf(bindingMap.get("knowledgeId"))));
        }
        return resolveVisibleKnowledgeViews(knowledgeIds).validKnowledges();
    }

    private ExtraKnowledgeResolution resolveExtraKnowledges(ChatSessionSummary summary) {
        ChatSessionExtPayload payload = chatJsonSupport.fromJson(summary.getExtJson(), ChatSessionExtPayload.class);
        List<BigInteger> extraKnowledgeIds = payload == null ? List.of() : payload.getExtraKnowledgeIds();
        return resolveVisibleKnowledgeViews(extraKnowledgeIds);
    }

    private ExtraKnowledgeResolution resolveVisibleKnowledgeViews(List<BigInteger> knowledgeIds) {
        if (knowledgeIds == null || knowledgeIds.isEmpty()) {
            return new ExtraKnowledgeResolution(List.of(), List.of(), List.of(), false);
        }
        List<BigInteger> normalizedIds = new ArrayList<>();
        for (BigInteger knowledgeId : knowledgeIds) {
            if (knowledgeId != null && !normalizedIds.contains(knowledgeId)) {
                normalizedIds.add(knowledgeId);
            }
        }
        if (normalizedIds.isEmpty()) {
            return new ExtraKnowledgeResolution(List.of(), List.of(), List.of(), false);
        }
        List<DocumentCollection> collections = documentCollectionService.listByIds(normalizedIds);
        Map<BigInteger, DocumentCollection> collectionMap = new LinkedHashMap<>();
        for (DocumentCollection collection : collections) {
            collectionMap.put(collection.getId(), collection);
        }
        KnowledgeReadAccessSnapshot accessSnapshot = knowledgeVisibilityQueryHelper.getCurrentReadSnapshot();
        List<ChatWorkspaceKnowledgeView> validKnowledges = new ArrayList<>();
        List<BigInteger> validKnowledgeIds = new ArrayList<>();
        List<String> removedNames = new ArrayList<>();
        boolean changed = false;
        for (BigInteger knowledgeId : normalizedIds) {
            DocumentCollection current = collectionMap.get(knowledgeId);
            if (current == null) {
                removedNames.add("知识库#" + knowledgeId);
                changed = true;
                continue;
            }
            if (PublishStatus.from(current.getPublishStatus()) != PublishStatus.PUBLISHED) {
                removedNames.add(current.getTitle());
                changed = true;
                continue;
            }
            if (!knowledgeVisibilityQueryHelper.canRead(current, accessSnapshot)) {
                removedNames.add(current.getTitle());
                changed = true;
                continue;
            }
            validKnowledges.add(toKnowledgeView(documentCollectionService.toPublishedView(current)));
            validKnowledgeIds.add(current.getId());
        }
        if (!Objects.equals(normalizedIds, validKnowledgeIds)) {
            changed = true;
        }
        return new ExtraKnowledgeResolution(validKnowledges, validKnowledgeIds, removedNames, changed);
    }

    private ChatWorkspaceKnowledgeView toKnowledgeView(DocumentCollection collection) {
        ChatWorkspaceKnowledgeView view = new ChatWorkspaceKnowledgeView();
        view.setId(collection.getId());
        view.setAlias(collection.getAlias());
        view.setTitle(collection.getTitle());
        view.setDescription(collection.getDescription());
        view.setIcon(collection.getIcon());
        return view;
    }

    private void syncSessionExtraKnowledges(ChatSessionSummary summary, List<BigInteger> validKnowledgeIds, BigInteger operatorId) {
        ChatSessionExtPayload payload = new ChatSessionExtPayload();
        payload.setExtraKnowledgeIds(validKnowledgeIds);
        ChatSessionUpsertCommand command = new ChatSessionUpsertCommand();
        command.setSessionId(summary.getId());
        command.setTenantId(summary.getTenantId());
        command.setDeptId(summary.getDeptId());
        command.setUserId(summary.getUserId());
        command.setUserAccount(summary.getUserAccount());
        command.setAssistantId(summary.getAssistantId());
        command.setAssistantCode(summary.getAssistantCode());
        command.setAssistantName(summary.getAssistantName());
        command.setTitle(summary.getTitle());
        command.setExtJson(chatJsonSupport.toJson(payload));
        command.setOperatorId(operatorId);
        command.setOperateAt(new Date());
        chatSessionCommandService.createOrTouchSession(command);
    }

    private String buildReadOnlyMessage(ChatWorkspaceReadOnlyReason reason) {
        if (reason == ChatWorkspaceReadOnlyReason.NO_PERMISSION) {
            return "当前会话对应的聊天助手已无权限访问，仅支持查看历史记录";
        }
        if (reason == ChatWorkspaceReadOnlyReason.ASSISTANT_OFFLINE) {
            return "当前会话对应的聊天助手已下架，无法继续聊天";
        }
        return "当前会话对应的聊天助手已删除，无法继续聊天";
    }

    private record AssistantAvailability(boolean continuable,
                                         ChatWorkspaceReadOnlyReason reason,
                                         Bot displayBot) {
    }

    private record ExtraKnowledgeResolution(List<ChatWorkspaceKnowledgeView> validKnowledges,
                                            List<BigInteger> validKnowledgeIds,
                                            List<String> removedNames,
                                            boolean shouldSync) {
    }
}
