package at.procon.dip.ingestion.service;

import at.procon.dip.domain.document.entity.DocumentMailAttachment;
import at.procon.dip.domain.document.entity.DocumentMailMessage;
import at.procon.dip.domain.document.entity.DocumentMailRecipient;
import at.procon.dip.domain.document.repository.DocumentMailAttachmentRepository;
import at.procon.dip.domain.document.repository.DocumentMailMessageRepository;
import at.procon.dip.domain.document.repository.DocumentMailRecipientRepository;
import at.procon.dip.ingestion.mail.MailProviderEnvelope;
import at.procon.ted.util.HashUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional
public class MailMetadataPersistenceService {

    private final DocumentMailMessageRepository mailMessageRepository;
    private final DocumentMailRecipientRepository recipientRepository;
    private final DocumentMailAttachmentRepository attachmentRepository;

    public void upsertMessageMetadata(UUID documentId,
                                      UUID sourceId,
                                      MailMessageExtractionService.ParsedMailMessage parsed,
                                      MailProviderEnvelope envelope,
                                      String rawMessageHash) {
        DocumentMailMessage entity = mailMessageRepository.findById(documentId)
                .orElse(DocumentMailMessage.builder().documentId(documentId).build());
        entity.setSourceId(sourceId);
        entity.setProviderType(envelope == null ? at.procon.dip.ingestion.mail.MailProviderType.GENERIC : envelope.providerType());
        entity.setAccountKey(envelope == null ? null : envelope.accountKey());
        entity.setFolderKey(envelope == null ? null : envelope.folderKey());
        entity.setProviderMessageKey(envelope == null ? null : envelope.providerMessageKey());
        entity.setProviderThreadKey(envelope == null ? null : envelope.providerThreadKey());
        entity.setMessageId(parsed.messageId());
        entity.setInReplyTo(parsed.inReplyTo());
        entity.setReferencesHeader(parsed.referencesHeader());
        entity.setThreadKey(resolveThreadKey(parsed, envelope));
        entity.setSubject(parsed.subject());
        entity.setNormalizedSubject(parsed.normalizedSubject());
        entity.setFromDisplayName(parsed.fromDisplayName());
        entity.setFromEmail(parsed.fromEmail());
        entity.setFromRaw(parsed.fromRaw());
        entity.setReplyToRaw(parsed.replyToRaw());
        entity.setSentAt(parsed.sentAt());
        entity.setReceivedAt(parsed.receivedAt());
        entity.setRawMessageHash(rawMessageHash);
        entity.setRawHeaderHash(HashUtils.computeSha256(buildHeaderFingerprint(parsed)));
        mailMessageRepository.save(entity);

        recipientRepository.deleteByMailDocumentId(documentId);
        List<DocumentMailRecipient> recipients = new ArrayList<>();
        for (MailMessageExtractionService.MailRecipient recipient : parsed.recipients()) {
            recipients.add(DocumentMailRecipient.builder()
                    .mailDocumentId(documentId)
                    .recipientType(recipient.recipientType())
                    .displayName(recipient.displayName())
                    .emailAddress(recipient.emailAddress())
                    .rawValue(recipient.rawValue())
                    .sortOrder(recipient.sortOrder())
                    .build());
        }
        recipientRepository.saveAll(recipients);
    }

    public void upsertAttachmentMetadata(UUID mailDocumentId,
                                         UUID attachmentDocumentId,
                                         MailMessageExtractionService.MailAttachment attachment,
                                         String extractionStatus,
                                         String errorMessage) {
        Optional<DocumentMailAttachment> existing = Optional.empty();
        if (attachment.attachmentIndex() != null) {
            existing = attachmentRepository.findByMailDocumentIdAndAttachmentIndex(mailDocumentId, attachment.attachmentIndex());
        }
        if (existing.isEmpty() && attachment.partPath() != null && !attachment.partPath().isBlank()) {
            existing = attachmentRepository.findByMailDocumentIdAndPartPath(mailDocumentId, attachment.partPath());
        }

        DocumentMailAttachment entity = existing.orElse(DocumentMailAttachment.builder()
                .mailDocumentId(mailDocumentId)
                .attachmentDocumentId(attachmentDocumentId)
                .build());
        entity.setAttachmentDocumentId(attachmentDocumentId);
        entity.setDisposition(attachment.disposition());
        entity.setContentId(attachment.contentId());
        entity.setFilename(attachment.fileName());
        entity.setMimeType(attachment.contentType());
        entity.setSizeBytes(attachment.sizeBytes());
        entity.setAttachmentIndex(attachment.attachmentIndex());
        entity.setPartPath(attachment.partPath());
        entity.setPathInArchive(attachment.path());
        entity.setExtractionStatus(extractionStatus == null || extractionStatus.isBlank() ? "IMPORTED" : extractionStatus);
        entity.setErrorMessage(errorMessage);
        attachmentRepository.save(entity);
    }

    private String resolveThreadKey(MailMessageExtractionService.ParsedMailMessage parsed, MailProviderEnvelope envelope) {
        if (envelope != null && envelope.providerThreadKey() != null && !envelope.providerThreadKey().isBlank()) {
            return envelope.providerThreadKey();
        }
        if (parsed.referencesHeader() != null && !parsed.referencesHeader().isBlank()) {
            String[] refs = parsed.referencesHeader().trim().split("\\s+");
            return refs.length == 0 ? parsed.referencesHeader() : refs[0];
        }
        if (parsed.inReplyTo() != null && !parsed.inReplyTo().isBlank()) {
            return parsed.inReplyTo();
        }
        if (parsed.messageId() != null && !parsed.messageId().isBlank()) {
            return parsed.messageId();
        }
        return parsed.normalizedSubject();
    }

    private String buildHeaderFingerprint(MailMessageExtractionService.ParsedMailMessage parsed) {
        return String.join("|",
                safe(parsed.subject()),
                safe(parsed.fromRaw()),
                safe(parsed.replyToRaw()),
                safe(parsed.messageId()),
                safe(parsed.inReplyTo()),
                safe(parsed.referencesHeader()),
                safe(parsed.sentAt() == null ? null : parsed.sentAt().toString()),
                safe(parsed.receivedAt() == null ? null : parsed.receivedAt().toString())
        );
    }

    private String safe(String value) {
        return value == null ? "" : value;
    }
}
