package cn.nianxx.thhotel.platform.message.service.impl;

import cn.nianxx.thhotel.platform.common.time.UtcTimeFormatter;
import cn.nianxx.thhotel.platform.message.common.dto.SourceMessageInboxSnapshot;
import cn.nianxx.thhotel.platform.message.common.dto.SourceMessageOriginalAccessAuditDraft;
import cn.nianxx.thhotel.platform.message.common.dto.SourceMessageOriginalContent;
import cn.nianxx.thhotel.platform.message.common.dto.SourceMessageOriginalMediaItem;
import cn.nianxx.thhotel.platform.message.common.enums.SourceMessageOriginalAccessResult;
import cn.nianxx.thhotel.platform.message.common.result.SourceMessageConversationMessageResult;
import cn.nianxx.thhotel.platform.message.common.result.SourceMessageConversationResult;
import cn.nianxx.thhotel.platform.message.common.result.SourceMessageConversationSummaryResult;
import cn.nianxx.thhotel.platform.message.common.result.SourceMessageOriginalMediaResponse;
import cn.nianxx.thhotel.platform.message.common.result.SourceMessageRelatedContextResult;
import cn.nianxx.thhotel.platform.message.repository.SourceMessageInboxRepository;
import cn.nianxx.thhotel.platform.message.service.SourceMessageConversationService;
import cn.nianxx.thhotel.platform.message.service.SourceMessageHtmlSanitizerService;
import cn.nianxx.thhotel.platform.message.service.SourceMessageRelatedContextProvider;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * SourceMessage 邮件会话详情服务实现。该服务负责受控读取原文并写入后端内部审计。
 */
@Service
public class SourceMessageConversationServiceImpl implements SourceMessageConversationService {

    private static final String INTERNAL_ACTOR_ID = "system:source-message-conversation";
    private static final String ACCESS_SCENE = "source-message-conversation";
    private static final String MEDIA_TYPE_INLINE_IMAGE = "INLINE_IMAGE";
    private static final String MEDIA_TYPE_ATTACHMENT = "ATTACHMENT";

    private final SourceMessageInboxRepository inboxRepository;
    private final List<SourceMessageRelatedContextProvider> relatedContextProviders;
    private final SourceMessageHtmlSanitizerService htmlSanitizerService;

    /**
     * 注入 SourceMessage 持久化端口和业务关联上下文 Provider 列表。
     */
    public SourceMessageConversationServiceImpl(
            SourceMessageInboxRepository inboxRepository,
            List<SourceMessageRelatedContextProvider> relatedContextProviders,
            SourceMessageHtmlSanitizerService htmlSanitizerService) {
        this.inboxRepository = inboxRepository;
        this.relatedContextProviders = relatedContextProviders == null ? List.of() : relatedContextProviders;
        this.htmlSanitizerService = htmlSanitizerService;
    }

    /**
     * 读取指定 SourceMessage 所在会话的完整原文链路，并为每封邮件写入内部读取审计。
     */
    @Override
    @Transactional
    public Optional<SourceMessageConversationResult> getConversation(Long sourceMessageId) {
        Optional<SourceMessageInboxSnapshot> sourceOptional = inboxRepository.findById(sourceMessageId);
        if (sourceOptional.isEmpty()) {
            return Optional.empty();
        }
        SourceMessageInboxSnapshot source = sourceOptional.get();
        List<SourceMessageInboxSnapshot> conversationMessages = findConversationMessages(source);
        SourceMessageConversationSummaryResult conversation = toConversationSummary(source, conversationMessages);
        List<SourceMessageConversationMessageResult> messages = conversationMessages.stream()
                .map(this::toConversationMessage)
                .toList();
        return Optional.of(new SourceMessageConversationResult(conversation, messages));
    }

    /**
     * 查询同一外部会话邮件；外部会话 ID 缺失时降级为单封邮件详情。
     */
    private List<SourceMessageInboxSnapshot> findConversationMessages(SourceMessageInboxSnapshot source) {
        if (source.externalConversationId() == null || source.externalConversationId().isBlank()) {
            return List.of(source);
        }
        List<SourceMessageInboxSnapshot> messages = inboxRepository.findByExternalConversationId(
                source.hotelId(),
                source.externalConversationId());
        return messages.isEmpty() ? List.of(source) : messages;
    }

    /**
     * 生成会话顶部摘要，时间统一转为 UTC OffsetDateTime。
     */
    private SourceMessageConversationSummaryResult toConversationSummary(
            SourceMessageInboxSnapshot source,
            List<SourceMessageInboxSnapshot> messages) {
        SourceMessageInboxSnapshot first = messages.get(0);
        SourceMessageInboxSnapshot last = messages.get(messages.size() - 1);
        return new SourceMessageConversationSummaryResult(
                source.hotelId(),
                source.channel(),
                source.externalConversationId(),
                source.subject(),
                (long) messages.size(),
                UtcTimeFormatter.toUtcOffsetDateTime(first.receivedAt()),
                UtcTimeFormatter.toUtcOffsetDateTime(last.receivedAt()));
    }

    /**
     * 将 Inbox 快照转换为会话邮件详情，并附带原文、媒体外链和业务关联摘要。
     */
    private SourceMessageConversationMessageResult toConversationMessage(SourceMessageInboxSnapshot message) {
        SourceMessageOriginalContent originalContent = readOriginalAndAudit(message);
        SourceMessageRelatedContextResult relatedContext = findRelatedContext(message);
        String htmlBody = originalContent.htmlBody();
        return new SourceMessageConversationMessageResult(
                message.id().toString(),
                message.externalMessageId(),
                message.externalConversationId(),
                message.senderSummary(),
                message.subject(),
                UtcTimeFormatter.toUtcOffsetDateTime(message.receivedAt()),
                UtcTimeFormatter.toUtcOffsetDateTime(message.sourceSentAt()),
                originalContent.textBody(),
                htmlBody,
                htmlSanitizerService.sanitizeHtml(htmlBody),
                true,
                htmlSanitizerService.htmlRenderMode(htmlBody),
                filterMedia(originalContent.mediaItems(), MEDIA_TYPE_INLINE_IMAGE),
                filterMedia(originalContent.mediaItems(), MEDIA_TYPE_ATTACHMENT),
                relatedContext.relatedOrders(),
                relatedContext.relatedTasks());
    }

    /**
     * 读取原文并写入内部审计。会话接口不依赖前端传原文读取 key。
     */
    private SourceMessageOriginalContent readOriginalAndAudit(SourceMessageInboxSnapshot message) {
        SourceMessageOriginalContent content = inboxRepository.findOriginalContent(message.id())
                .orElse(new SourceMessageOriginalContent(message.id(), null, null, List.of()));
        LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
        inboxRepository.insertOriginalAccessAudit(new SourceMessageOriginalAccessAuditDraft(
                message.id(),
                INTERNAL_ACTOR_ID,
                ACCESS_SCENE,
                SourceMessageOriginalAccessResult.GRANTED.code(),
                now));
        return content;
    }

    /**
     * 聚合所有业务模块提供的关联上下文。当前 reservation 模块会提供订单和任务摘要。
     */
    private SourceMessageRelatedContextResult findRelatedContext(SourceMessageInboxSnapshot message) {
        List<SourceMessageRelatedContextResult> contexts = relatedContextProviders.stream()
                .map(provider -> provider.findRelatedContext(message.hotelId(), message.id()))
                .toList();
        return new SourceMessageRelatedContextResult(
                contexts.stream().flatMap(context -> context.relatedOrders().stream()).toList(),
                contexts.stream().flatMap(context -> context.relatedTasks().stream()).toList());
    }

    /**
     * 按媒体类型筛选并转换原文媒体引用。
     */
    private List<SourceMessageOriginalMediaResponse> filterMedia(
            List<SourceMessageOriginalMediaItem> mediaItems,
            String mediaType) {
        if (mediaItems == null || mediaItems.isEmpty()) {
            return List.of();
        }
        return mediaItems.stream()
                .filter(item -> mediaType.equals(item.mediaType()))
                .map(this::toMediaResponse)
                .toList();
    }

    /**
     * 转换媒体内部 DTO 为接口响应对象。
     */
    private SourceMessageOriginalMediaResponse toMediaResponse(SourceMessageOriginalMediaItem item) {
        return new SourceMessageOriginalMediaResponse(
                item.mediaType(),
                item.fileName(),
                item.contentType(),
                item.sizeBytes(),
                item.externalUrl(),
                item.externalMediaId());
    }

}
