using System;
using System.Linq;
using MailKit;
using MimeKit;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Enums;
using Wino.Services.Extensions;

namespace Wino.Services.Extensions
{
    public static class MailkitClientExtensions
    {
        public static char MailCopyUidSeparator = '_';

        public static uint ResolveUid(string mailCopyId)
        {
            var splitted = mailCopyId.Split(MailCopyUidSeparator);

            if (splitted.Length > 1 && uint.TryParse(splitted[1], out uint parsedUint)) return parsedUint;

            throw new ArgumentOutOfRangeException(nameof(mailCopyId), mailCopyId, "Invalid mailCopyId format.");
        }

        public static string CreateUid(Guid folderId, uint messageUid)
            => $"{folderId}{MailCopyUidSeparator}{messageUid}";

        public static MailImportance GetImportance(this MimeMessage messageSummary)
        {
            if (messageSummary.Headers != null && messageSummary.Headers.Contains(HeaderId.Importance))
            {
                var rawImportance = messageSummary.Headers[HeaderId.Importance];

                return rawImportance switch
                {
                    "Low" => MailImportance.Low,
                    "High" => MailImportance.High,
                    _ => MailImportance.Normal,
                };
            }

            return MailImportance.Normal;
        }

        public static bool GetIsRead(this MessageFlags? flags)
            => flags.GetValueOrDefault().HasFlag(MessageFlags.Seen);

        public static bool GetIsFlagged(this MessageFlags? flags)
            => flags.GetValueOrDefault().HasFlag(MessageFlags.Flagged);

        public static string GetThreadId(this IMessageSummary messageSummary)
        {
            // First check whether we have the default values.

            if (!string.IsNullOrEmpty(messageSummary.ThreadId))
                return messageSummary.ThreadId;

            if (messageSummary.GMailThreadId != null)
                return messageSummary.GMailThreadId.ToString();

            return default;
        }

        public static string GetMessageId(this MimeMessage mimeMessage)
            => mimeMessage.MessageId;

        public static string GetReferences(this MessageIdList messageIdList)
            => string.Join(";", messageIdList);

        public static string GetInReplyTo(this MimeMessage mimeMessage)
        {
            if (mimeMessage.Headers.Contains(HeaderId.InReplyTo))
            {
                // Normalize if <> brackets are there.
                var inReplyTo = mimeMessage.Headers[HeaderId.InReplyTo];

                if (inReplyTo.StartsWith("<") && inReplyTo.EndsWith(">"))
                    return inReplyTo.Substring(1, inReplyTo.Length - 2);

                return inReplyTo;
            }

            return string.Empty;
        }

        private static string GetPreviewText(this MimeMessage message)
        {
            if (string.IsNullOrEmpty(message.HtmlBody))
                return message.TextBody;
            else
                return HtmlAgilityPackExtensions.GetPreviewText(message.HtmlBody);
        }

        public static MailCopy GetMailDetails(this IMessageSummary messageSummary, MailItemFolder folder, MimeMessage mime)
        {
            // MessageSummary will only have UniqueId, Flags, ThreadId.
            // Other properties are extracted directly from the MimeMessage.

            // IMAP doesn't have unique id for mails.
            // All mails are mapped to specific folders with incremental Id.
            // Uid 1 may belong to different messages in different folders, but can never be
            // same for different messages in same folders.
            // Here we create arbitrary Id that maps the Id of the message with Folder UniqueId.
            // When folder becomes invalid, we'll clear out these MailCopies as well.

            var messageUid = CreateUid(folder.Id, messageSummary.UniqueId.Id);
            var previewText = mime.GetPreviewText();

            var copy = new MailCopy()
            {
                Id = messageUid,
                CreationDate = mime.Date.UtcDateTime,
                ThreadId = messageSummary.GetThreadId(),
                MessageId = mime.GetMessageId(),
                Subject = mime.Subject,
                IsRead = messageSummary.Flags.GetIsRead(),
                IsFlagged = messageSummary.Flags.GetIsFlagged(),
                PreviewText = previewText,
                FromAddress = GetActualSenderAddress(mime),
                FromName = GetActualSenderName(mime),
                IsFocused = false,
                Importance = mime.GetImportance(),
                References = mime.References?.GetReferences(),
                InReplyTo = mime.GetInReplyTo(),
                HasAttachments = mime.Attachments.Any(),
                FileId = Guid.NewGuid()
            };

            return copy;
        }

        // TODO: Name and Address parsing should be handled better.
        // At some point Wino needs better contact management.

        public static string GetActualSenderName(MimeMessage message)
        {
            if (message == null)
                return string.Empty;

            return message.From.Mailboxes.FirstOrDefault()?.Name ?? message.Sender?.Name ?? Translator.UnknownSender;

            // From MimeKit

            // The "From" header specifies the author(s) of the message.
            // If more than one MimeKit.MailboxAddress is added to the list of "From" addresses,
            // the MimeKit.MimeMessage.Sender should be set to the single MimeKit.MailboxAddress
            // of the personal actually sending the message.

            // Also handle: https://stackoverflow.com/questions/46474030/mailkit-from-address

            //if (message.Sender != null)
            //    return string.IsNullOrEmpty(message.Sender.Name) ? message.Sender.Address : message.Sender.Name;
            //else if (message.From?.Mailboxes != null)
            //{
            //    var firstAvailableName = message.From.Mailboxes.FirstOrDefault(a => !string.IsNullOrEmpty(a.Name))?.Name;

            //    if (string.IsNullOrEmpty(firstAvailableName))
            //    {
            //        var firstAvailableAddress = message.From.Mailboxes.FirstOrDefault(a => !string.IsNullOrEmpty(a.Address))?.Address;

            //        if (!string.IsNullOrEmpty(firstAvailableAddress))
            //        {
            //            return firstAvailableAddress;
            //        }
            //    }

            //    return firstAvailableName;
            //}

            //// No sender, no from, I don't know what to do.
            //return Translator.UnknownSender;
        }

        // TODO: This is wrong.
        public static string GetActualSenderAddress(MimeMessage message)
        {
            return message.From.Mailboxes.FirstOrDefault()?.Address ?? message.Sender?.Address ?? Translator.UnknownSender;
            //if (mime == null)
            //    return string.Empty;

            //bool hasSingleFromMailbox = mime.From.Mailboxes.Count() == 1;

            //if (hasSingleFromMailbox)
            //    return mime.From.Mailboxes.First().GetAddress(idnEncode: true);
            //else if (mime.Sender != null)
            //    return mime.Sender.GetAddress(idnEncode: true);
            //else
            //    return Translator.UnknownSender;
        }
    }
}
