using FluentAssertions;
using MimeKit;
using Moq;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Extensions;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Tests.Helpers;
using Wino.Services;
using Xunit;

namespace Wino.Core.Tests.Services;

public class MailThreadingTests : IAsyncLifetime
{
    private InMemoryDatabaseService _databaseService = null!;
    private MailService _mailService = null!;
    private MailAccount _account = null!;
    private MailItemFolder _draftFolder = null!;

    public async Task InitializeAsync()
    {
        _databaseService = new InMemoryDatabaseService();
        await _databaseService.InitializeAsync();

        _account = new MailAccount
        {
            Id = Guid.NewGuid(),
            Name = "Threading Test Account",
            Address = "me@test.local",
            SenderName = "Test User",
            ProviderType = MailProviderType.IMAP4
        };

        _draftFolder = new MailItemFolder
        {
            Id = Guid.NewGuid(),
            MailAccountId = _account.Id,
            FolderName = "Drafts",
            SpecialFolderType = SpecialFolderType.Draft,
            IsSystemFolder = true,
            IsSynchronizationEnabled = true
        };

        var preferences = new MailAccountPreferences
        {
            Id = Guid.NewGuid(),
            AccountId = _account.Id,
            IsNotificationsEnabled = true,
            IsSignatureEnabled = false
        };

        var alias = new MailAccountAlias
        {
            Id = Guid.NewGuid(),
            AccountId = _account.Id,
            AliasAddress = _account.Address,
            ReplyToAddress = _account.Address,
            IsPrimary = true,
            IsRootAlias = true,
            IsVerified = true
        };

        await _databaseService.Connection.InsertAsync(_account, typeof(MailAccount));
        await _databaseService.Connection.InsertAsync(_draftFolder, typeof(MailItemFolder));
        await _databaseService.Connection.InsertAsync(preferences, typeof(MailAccountPreferences));
        await _databaseService.Connection.InsertAsync(alias, typeof(MailAccountAlias));

        _mailService = BuildMailService(_databaseService);
    }

    public async Task DisposeAsync() => await _databaseService.DisposeAsync();

    [Fact]
    public async Task CreateDraftAsync_EmptyDraft_AssignsGeneratedMessageId()
    {
        var (draftMailCopy, draftBase64MimeMessage) = await _mailService.CreateDraftAsync(
            _account.Id,
            new DraftCreationOptions { Reason = DraftCreationReason.Empty });

        var mimeMessage = draftBase64MimeMessage.GetMimeMessageFromBase64();

        draftMailCopy.MessageId.Should().MatchRegex("^[0-9a-fA-F-]{36}@wino-mail\\.app$");
        mimeMessage.MessageId.Should().Be(draftMailCopy.MessageId);
        mimeMessage.Headers[HeaderId.MessageId].Should().Be(MailHeaderExtensions.ToHeaderMessageId(draftMailCopy.MessageId));
    }

    [Fact]
    public async Task CreateDraftAsync_Reply_SetsInReplyToReferencesAndReplySubject()
    {
        const string parentMessageId = "original@domain.com";

        var referencedMimeMessage = CreateReferencedMimeMessage("From outlook", parentMessageId);
        var referencedMailCopy = new MailCopy
        {
            UniqueId = Guid.NewGuid(),
            Id = Guid.NewGuid().ToString(),
            ThreadId = "provider-thread-id",
            MessageId = parentMessageId
        };

        var (draftMailCopy, draftBase64MimeMessage) = await _mailService.CreateDraftAsync(
            _account.Id,
            new DraftCreationOptions
            {
                Reason = DraftCreationReason.Reply,
                ReferencedMessage = new ReferencedMessage
                {
                    MimeMessage = referencedMimeMessage,
                    MailCopy = referencedMailCopy
                }
            });

        var mimeMessage = draftBase64MimeMessage.GetMimeMessageFromBase64();

        draftMailCopy.InReplyTo.Should().Be(parentMessageId);
        draftMailCopy.References.Should().Be(parentMessageId);
        draftMailCopy.Subject.Should().Be("Re: From outlook");
        draftMailCopy.ThreadId.Should().Be(referencedMailCopy.ThreadId);

        mimeMessage.InReplyTo.Should().Be(parentMessageId);
        MailHeaderExtensions.NormalizeReferences(mimeMessage.Headers[HeaderId.References]).Should().Be(parentMessageId);
    }

    [Fact]
    public async Task CreateDraftAsync_Reply_AppendsReferencesChainOnce()
    {
        const string rootMessageId = "root@domain.com";
        const string middleMessageId = "middle@domain.com";
        const string parentMessageId = "parent@domain.com";

        var referencedMimeMessage = CreateReferencedMimeMessage("Re: Existing subject", parentMessageId);
        referencedMimeMessage.References.Add(rootMessageId);
        referencedMimeMessage.References.Add(middleMessageId);

        var (draftMailCopy, draftBase64MimeMessage) = await _mailService.CreateDraftAsync(
            _account.Id,
            new DraftCreationOptions
            {
                Reason = DraftCreationReason.Reply,
                ReferencedMessage = new ReferencedMessage
                {
                    MimeMessage = referencedMimeMessage,
                    MailCopy = new MailCopy { UniqueId = Guid.NewGuid(), Id = Guid.NewGuid().ToString(), MessageId = parentMessageId }
                }
            });

        var mimeMessage = draftBase64MimeMessage.GetMimeMessageFromBase64();

        draftMailCopy.References.Should().Be($"{rootMessageId};{middleMessageId};{parentMessageId}");
        draftMailCopy.Subject.Should().Be("Re: Existing subject");
        MailHeaderExtensions.NormalizeReferences(mimeMessage.Headers[HeaderId.References])
            .Should().Be($"{rootMessageId};{middleMessageId};{parentMessageId}");
    }

    [Fact]
    public async Task CreateDraftAsync_Reply_FallsBackToReferencedMailCopyThreadingMetadata()
    {
        const string rootMessageId = "root@domain.com";
        const string parentMessageId = "copy-parent@domain.com";

        var referencedMimeMessage = CreateReferencedMimeMessage("Fallback subject");
        referencedMimeMessage.Headers.Remove(HeaderId.MessageId);

        var referencedMailCopy = new MailCopy
        {
            UniqueId = Guid.NewGuid(),
            Id = Guid.NewGuid().ToString(),
            MessageId = parentMessageId,
            References = rootMessageId
        };

        var (draftMailCopy, _) = await _mailService.CreateDraftAsync(
            _account.Id,
            new DraftCreationOptions
            {
                Reason = DraftCreationReason.Reply,
                ReferencedMessage = new ReferencedMessage
                {
                    MimeMessage = referencedMimeMessage,
                    MailCopy = referencedMailCopy
                }
            });

        draftMailCopy.InReplyTo.Should().Be(parentMessageId);
        draftMailCopy.References.Should().Be($"{rootMessageId};{parentMessageId}");
    }

    private static MimeMessage CreateReferencedMimeMessage(string subject, string? messageId = null)
    {
        var message = new MimeMessage();
        message.From.Add(new MailboxAddress("Sender", "sender@example.com"));
        message.To.Add(new MailboxAddress("Recipient", "recipient@example.com"));
        message.Subject = subject;
        message.Body = new TextPart("plain") { Text = "Body" };

        if (!string.IsNullOrWhiteSpace(messageId))
            message.MessageId = messageId;

        return message;
    }

    private static MailService BuildMailService(InMemoryDatabaseService db)
    {
        var signatureService = new Mock<ISignatureService>();
        var authProvider = new Mock<IAuthenticationProvider>();
        var mimeFileService = new Mock<IMimeFileService>();
        mimeFileService
            .Setup(x => x.SaveMimeMessageAsync(It.IsAny<Guid>(), It.IsAny<MimeMessage>(), It.IsAny<Guid>()))
            .ReturnsAsync(true);
        mimeFileService
            .Setup(x => x.CreateHTMLPreviewVisitor(It.IsAny<MimeMessage>(), It.IsAny<string>()))
            .Returns<MimeMessage, string>((_, _) => new HtmlPreviewVisitor(string.Empty));

        var preferencesService = new Mock<IPreferencesService>();
        preferencesService.SetupProperty(x => x.ComposerFont, "Calibri");
        preferencesService.SetupProperty(x => x.ComposerFontSize, 12);

        var contactPictureFileService = new Mock<IContactPictureFileService>();

        var accountService = new AccountService(
            db,
            signatureService.Object,
            authProvider.Object,
            mimeFileService.Object,
            preferencesService.Object,
            contactPictureFileService.Object);

        var folderService = new FolderService(db, accountService);
        var contactService = new ContactService(db);
        var sentMailReceiptService = new SentMailReceiptService(db, folderService, accountService);

        return new MailService(
            db,
            folderService,
            contactService,
            accountService,
            signatureService.Object,
            mimeFileService.Object,
            preferencesService.Object,
            sentMailReceiptService);
    }
}
