using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.IO;

using MailKit.Net.Smtp;
using MailKit.Net.Imap;
using MailKit.Search;
using MailKit;
using MimeKit;
using MimeKit.Utils;

namespace rockfishCore.Util
{
    //http://www.mimekit.net/
    public static class RfMail
    {

        public const string MAIL_MIRROR_SMPT_ADDRESS = "mail2.ayanova.com";
        public const string MAIL_MIRROR_IMAP_ADDRESS = "mail2.ayanova.com";

        public const string MAIL_SMPT_ADDRESS = "mail.ayanova.com";
        public const int MAIL_SMPT_PORT = 465;
        public const string MAIL_IMAP_ADDRESS = "mail.ayanova.com";
        //public const int MAIL_IMAP_PORT = 993;
        //testing
        public const int MAIL_IMAP_PORT = 143;

        public const string MAIL_ACCOUNT_SUPPORT = "support@ayanova.com";
        public const string MAIL_ACCOUNT_PASSWORD_SUPPORT = "e527b6c5a00c27bb61ca694b3de0ee178cbe3f1541a772774762ed48e9caf5ce";

        public const string MAIL_ACCOUNT_SALES = "sales@ayanova.com";
        public const string MAIL_ACCOUNT_PASSWORD_SALES = "6edbae5eb616a975abf86bcd9f45616f5c70c4f05189af60a1caaa62b406149d";

        public enum rfMailAccount
        {
            support = 1,
            sales = 2
        }


        public class rfMailMessage
        {
            public MimeMessage message;
            public uint uid;
        }


        class DeliverStatusSmtpClient : SmtpClient
        {
            protected override DeliveryStatusNotification? GetDeliveryStatusNotifications(MimeMessage message, MailboxAddress mailbox)
            {
                return DeliveryStatusNotification.Delay |
                       DeliveryStatusNotification.Failure |
                       DeliveryStatusNotification.Success;

            }
        }


        /////////////////////////////////////////////////////////
        //
        //  Do the sending with optional deliver status receipt
        //
        public static void DoSend(MimeMessage message, string MailAccount, string MailAccountPassword, bool trackDeliveryStatus)
        {

            if (trackDeliveryStatus)
            {
                //set the return receipt and disposition to headers
                message.Headers.Add("Return-Receipt-To", "<" + MailAccount + ">");
                message.Headers.Add("Disposition-Notification-To", "<" + MailAccount + ">");
                using (var client = new DeliverStatusSmtpClient())
                {
                    //Accept all SSL certificates (in case the server supports STARTTLS)
                    //(we have a funky cert on the mail server)
                    client.ServerCertificateValidationCallback = (s, c, h, e) => true;
                    client.Connect(MAIL_SMPT_ADDRESS, MAIL_SMPT_PORT);

                    // Note: since we don't have an OAuth2 token, disable
                    // the XOAUTH2 authentication mechanism.
                    client.AuthenticationMechanisms.Remove("XOAUTH2");

                    // Note: only needed if the SMTP server requires authentication
                    client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);

                    client.Send(message);
                    client.Disconnect(true);
                }
            }
            else
            {
                using (var client = new SmtpClient())
                {
                    //Accept all SSL certificates (in case the server supports STARTTLS)
                    //(we have a funky cert on the mail server)
                    client.ServerCertificateValidationCallback = (s, c, h, e) => true;
                    client.Connect(MAIL_SMPT_ADDRESS, MAIL_SMPT_PORT);

                    // Note: since we don't have an OAuth2 token, disable
                    // the XOAUTH2 authentication mechanism.
                    client.AuthenticationMechanisms.Remove("XOAUTH2");

                    // Note: only needed if the SMTP server requires authentication
                    client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);

                    client.Send(message);
                    client.Disconnect(true);
                }
            }
        }




        public static void SendMessage(string MessageFrom, string MessageTo, string MessageSubject, string MessageBody,
         bool trackDeliveryStatus = false, string CustomHeader = "", string CustomHeaderValue = "")
        {
            var message = new MimeMessage();
            message.From.Add(new MailboxAddress(MessageFrom));
            message.To.Add(new MailboxAddress(MessageTo));
            message.Subject = MessageSubject;

            message.Body = new TextPart("plain")
            {
                Text = MessageBody
            };

            message.Headers["X-Mailer"] = RfVersion.Full;

            if (CustomHeader != "" && CustomHeaderValue != "")
            {
                message.Headers["X-Rockfish-" + CustomHeader] = CustomHeaderValue;
            }

            //send with optional tracking
            DoSend(message, MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT, trackDeliveryStatus);

            // using (var client = new SmtpClient())
            // {
            //     //Accept all SSL certificates (in case the server supports STARTTLS)
            //     //(we have a funky cert on the mail server)
            //     client.ServerCertificateValidationCallback = (s, c, h, e) => true;
            //     client.Connect(MAIL_SMPT_ADDRESS, MAIL_SMPT_PORT, true);

            //     // Note: since we don't have an OAuth2 token, disable
            //     // the XOAUTH2 authentication mechanism.
            //     client.AuthenticationMechanisms.Remove("XOAUTH2");

            //     // Note: only needed if the SMTP server requires authentication
            //     client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);

            //     client.Send(message);
            //     client.Disconnect(true);
            // }
        }//send message


        #region handle spam message



        /// <summary>
        /// Forward a message to the isspam@ayanova.com address and then erase it
        /// </summary>
        /// <param name="spamMessageId"></param>
        /// <param name="replyFromAccount"></param>
        public static void HandleSpamMessage(uint spamMessageId, rfMailAccount replyFromAccount)
        {
            using (var client = new ImapClient())
            {
                // Accept all SSL certificates
                client.ServerCertificateValidationCallback = (s, c, h, e) => true;
                client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT);
                // Note: since we don't have an OAuth2 token, disable
                // the XOAUTH2 authentication mechanism.
                client.AuthenticationMechanisms.Remove("XOAUTH2");
                if (replyFromAccount == rfMailAccount.support)
                {
                    client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);
                }
                else
                {
                    client.Authenticate(MAIL_ACCOUNT_SALES, MAIL_ACCOUNT_PASSWORD_SALES);
                }

                var spamFolder = client.GetFolder("Spam");

                if (spamFolder != null)
                {
                    client.Inbox.Open(FolderAccess.ReadWrite);
                    client.Inbox.MoveTo(new UniqueId(spamMessageId), spamFolder);
                }


                client.Disconnect(true);
            }
        }//spaminator

        #endregion




        public static void ReplyMessage(uint replyToMessageId, rfMailAccount replyFromAccount, string replyBody,
        bool replyToAll, bool trackDeliveryStatus = false, string CustomHeader = "", string CustomHeaderValue = "")
        {
            //get the original to reply to it:
            MimeMessage message = GetMessage(replyToMessageId, replyFromAccount);
            if (message == null)
            {
                throw new System.ArgumentException("RfMail:ReplyMessage->source message not found (id=" + replyToMessageId.ToString() + ")");
            }
            //construct the new message

            var reply = new MimeMessage();
            MailboxAddress from = null;
            string from_account = "";
            string from_account_password = "";
            switch (replyFromAccount)
            {
                case rfMailAccount.sales:
                    from_account = MAIL_ACCOUNT_SALES;
                    from_account_password = MAIL_ACCOUNT_PASSWORD_SALES;
                    from = new MailboxAddress(from_account);
                    break;
                default:
                    from_account = MAIL_ACCOUNT_SUPPORT;
                    from_account_password = MAIL_ACCOUNT_PASSWORD_SUPPORT;
                    from = new MailboxAddress(from_account);
                    break;
            }
            reply.From.Add(from);

            // reply to the sender of the message
            if (message.ReplyTo.Count > 0)
            {
                reply.To.AddRange(message.ReplyTo);
            }
            else if (message.From.Count > 0)
            {
                reply.To.AddRange(message.From);
            }
            else if (message.Sender != null)
            {
                reply.To.Add(message.Sender);
            }

            if (replyToAll)
            {
                // include all of the other original recipients (removing ourselves from the list)
                reply.To.AddRange(message.To.Mailboxes.Where(x => x.Address != from.Address));
                reply.Cc.AddRange(message.Cc.Mailboxes.Where(x => x.Address != from.Address));
            }


            // set the reply subject
            if (!message.Subject.StartsWith("Re:", StringComparison.OrdinalIgnoreCase))
                reply.Subject = "Re: " + message.Subject;
            else
                reply.Subject = message.Subject;

            // construct the In-Reply-To and References headers
            if (!string.IsNullOrEmpty(message.MessageId))
            {
                reply.InReplyTo = message.MessageId;
                foreach (var id in message.References)
                    reply.References.Add(id);
                reply.References.Add(message.MessageId);
            }

            // quote the original message text
            using (var quoted = new StringWriter())
            {
                var sender = message.Sender ?? message.From.Mailboxes.FirstOrDefault();
                var name = sender != null ? (!string.IsNullOrEmpty(sender.Name) ? sender.Name : sender.Address) : "someone";

                quoted.WriteLine("On {0}, {1} wrote:", message.Date.ToString("f"), name);
                using (var reader = new StringReader(message.TextBody))
                {
                    string line;

                    while ((line = reader.ReadLine()) != null)
                    {
                        quoted.Write("> ");
                        quoted.WriteLine(line);
                    }
                }

                reply.Body = new TextPart("plain")
                {
                    Text = replyBody + "\r\n\r\n\r\n" + quoted.ToString()
                };
            }

            reply.Headers["X-Mailer"] = RfVersion.Full;

            if (CustomHeader != "" && CustomHeaderValue != "")
            {
                reply.Headers["X-Rockfish-" + CustomHeader] = CustomHeaderValue;
            }


            //send with optional tracking
            DoSend(reply, from_account, from_account_password, trackDeliveryStatus);

            //flag the message as having been replied to
            FlagInboxMessageSeenReplied(replyToMessageId, replyFromAccount);

        }//send message


        //Fetch message by UID
        public static MimeMessage GetMessage(uint uid, rfMailAccount fromAccount = rfMailAccount.support)
        {
            using (var client = new ImapClient())
            {
                // Accept all SSL certificates
                client.ServerCertificateValidationCallback = (s, c, h, e) => true;
                client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT);
                // Note: since we don't have an OAuth2 token, disable
                // the XOAUTH2 authentication mechanism.
                client.AuthenticationMechanisms.Remove("XOAUTH2");
                if (fromAccount == rfMailAccount.support)
                {
                    client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);
                }
                else
                {
                    client.Authenticate(MAIL_ACCOUNT_SALES, MAIL_ACCOUNT_PASSWORD_SALES);
                }

                var inbox = client.Inbox;
                inbox.Open(FolderAccess.ReadOnly);
                var m = inbox.GetMessage(new UniqueId(uid));
                client.Disconnect(true);
                return m;
            }
        }//get message



        //Fetch messages by Search query
        public static List<rfMailMessage> GetMessages(SearchQuery query)
        {
            List<rfMailMessage> ret = new List<rfMailMessage>();
            using (var client = new ImapClient())
            {
                // Accept all SSL certificates
                client.ServerCertificateValidationCallback = (s, c, h, e) => true;
                client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT);
                // Note: since we don't have an OAuth2 token, disable
                // the XOAUTH2 authentication mechanism.
                client.AuthenticationMechanisms.Remove("XOAUTH2");
                client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);

                var inbox = client.Inbox;
                inbox.Open(FolderAccess.ReadOnly);
                foreach (var uid in inbox.Search(query))
                {
                    ret.Add(new rfMailMessage { message = inbox.GetMessage(uid), uid = uid.Id });
                }
                client.Disconnect(true);
            }
            return ret;
        }//get message


        //Flag message as seen and replied by UID
        public static bool FlagInboxMessageSeenReplied(uint uid, rfMailAccount inAccount = rfMailAccount.support)
        {
            using (var client = new ImapClient())
            {
                // Accept all SSL certificates
                client.ServerCertificateValidationCallback = (s, c, h, e) => true;
                client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT);
                // Note: since we don't have an OAuth2 token, disable
                // the XOAUTH2 authentication mechanism.
                client.AuthenticationMechanisms.Remove("XOAUTH2");

                if (inAccount == rfMailAccount.support)
                {
                    client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);
                }
                else
                {
                    client.Authenticate(MAIL_ACCOUNT_SALES, MAIL_ACCOUNT_PASSWORD_SALES);
                }

                var inbox = client.Inbox;
                inbox.Open(FolderAccess.ReadWrite);
                inbox.AddFlags(new UniqueId(uid), MessageFlags.Seen, true);
                inbox.AddFlags(new UniqueId(uid), MessageFlags.Answered, true);
                client.Disconnect(true);
                return true;
            }
        }//get message


        ////////////////////////////////////////////////////
        //Put a message in the drafts folder of support
        //
        public static void DraftMessage(string MessageFrom, string MessageTo, string MessageSubject, string MessageBody,
         string CustomHeader = "", string CustomHeaderValue = "")
        {
            var message = new MimeMessage();
            message.From.Add(new MailboxAddress(MessageFrom));


            //case 3512 handle more than one email in the address
            if (MessageTo.Contains(","))
            {
                List<MailboxAddress> mbAll = new List<MailboxAddress>();
                var addrs = MessageTo.Split(',');
                foreach (string addr in addrs)
                {
                    mbAll.Add(new MailboxAddress(addr.Trim()));
                }
                message.To.AddRange(mbAll);
            }
            else
            {
                message.To.Add(new MailboxAddress(MessageTo));
            }


            message.Subject = MessageSubject;

            message.Body = new TextPart("plain")
            {
                Text = MessageBody
            };

            if (CustomHeader != "" && CustomHeaderValue != "")
            {
                message.Headers["X-Rockfish-" + CustomHeader] = CustomHeaderValue;
            }

            //adapted from https://stackoverflow.com/questions/33365072/mailkit-sending-drafts
            using (var client = new ImapClient())
            {
                try
                {
                    // Accept all SSL certificates
                    client.ServerCertificateValidationCallback = (s, c, h, e) => true;
                    client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT);
                    // Note: since we don't have an OAuth2 token, disable
                    // the XOAUTH2 authentication mechanism.
                    client.AuthenticationMechanisms.Remove("XOAUTH2");
                    client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);


                    var draftFolder = client.GetFolder("Drafts");//Our surgemail server works with this other servers in future might not
                    if (draftFolder != null)
                    {
                        draftFolder.Open(FolderAccess.ReadWrite);
                        draftFolder.Append(message, MessageFlags.Draft);
                        //draftFolder.Expunge();
                    }
                }
                catch (Exception ex)
                {
                    throw new System.Exception("RfMail->DraftMessage() - Exception has occured: " + ex.Message);
                }

                client.Disconnect(true);
            }

        }//draft message


        /////////////////////////////////////////////////////////////////
        //Fetch summaries of unread messages in sales and support 
        //inboxes 
        //
        public static List<rfMessageSummary> GetSalesAndSupportSummaries()
        {
            List<rfMessageSummary> ret = new List<rfMessageSummary>();

            ret.AddRange(getInboxSummariesFor(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT));
            ret.AddRange(getInboxSummariesFor(MAIL_ACCOUNT_SALES, MAIL_ACCOUNT_PASSWORD_SALES));

            return ret;
        }

        private static List<rfMessageSummary> getInboxSummariesFor(string sourceAccount, string sourcePassword)
        {
            //Console.WriteLine($"getInboxSummariesFor {sourceAccount}");
            List<rfMessageSummary> ret = new List<rfMessageSummary>();
            using (var client = new ImapClient())
            {
                // Accept all SSL certificates
                client.ServerCertificateValidationCallback = (s, c, h, e) => true;

                //client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT, true);
                //testing

                //Console.WriteLine($"connecting to {MAIL_IMAP_ADDRESS} on {MAIL_IMAP_PORT}");
                client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT);

                // Note: since we don't have an OAuth2 token, disable
                // the XOAUTH2 authentication mechanism.
                //Console.WriteLine($"removing XOAUTH2");
                client.AuthenticationMechanisms.Remove("XOAUTH2");

                //Console.WriteLine($"authenticating...");
                client.Authenticate(sourceAccount, sourcePassword);

                var inbox = client.Inbox;
                //Console.WriteLine($"opening inbox...");
                inbox.Open(FolderAccess.ReadOnly);
                //Console.WriteLine($"fetching summaries...");
                var summaries = inbox.Fetch(0, -1, MessageSummaryItems.Full | MessageSummaryItems.UniqueId);
                //Console.WriteLine($"disconnecting...");
                client.Disconnect(true);
                foreach (var summary in summaries)
                {
                    //Sometimes bad hombres don't set a from address so don't expect one
                    string sFrom = "UNKNOWN / NOT SET";
                    if (summary.Envelope.From.Count > 0)
                    {
                        sFrom = summary.Envelope.From[0].ToString().Replace("\"", "").Replace("<", "").Replace(">", "").Trim();
                    }
                    ret.Add(new rfMessageSummary
                    {
                        account = sourceAccount,
                        id = summary.UniqueId.Id,
                        subject = summary.Envelope.Subject,
                        sent = DateUtil.DateTimeOffSetNullableToEpoch(summary.Envelope.Date),
                        from = sFrom,
                        flags = summary.Flags.ToString().ToLowerInvariant()
                    });
                }
            }
            //reverse the results array as emails come in oldest first order but we want oldest last
            ret.Reverse();
            return ret;
        }


        public class rfMessageSummary
        {
            public string account;
            public uint id;
            public string from;
            public string subject;
            public long? sent;
            public string flags;
        }

        //Fetch a single string preview of message by Account / folder / UID
        public static rfMessagePreview GetMessagePreview(string mailAccount, string mailFolder, uint uid)
        {
            using (var client = new ImapClient())
            {
                // Accept all SSL certificates
                client.ServerCertificateValidationCallback = (s, c, h, e) => true;
                //client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT, true);
                client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT);
                // Note: since we don't have an OAuth2 token, disable
                // the XOAUTH2 authentication mechanism.
                client.AuthenticationMechanisms.Remove("XOAUTH2");

                //TODO: make accounts reside in dictionary in future
                if (mailAccount == "support@ayanova.com")
                {
                    client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);
                }
                if (mailAccount == "sales@ayanova.com")
                {
                    client.Authenticate(MAIL_ACCOUNT_SALES, MAIL_ACCOUNT_PASSWORD_SALES);
                }

                var fldr = client.GetFolder(mailFolder);
                fldr.Open(FolderAccess.ReadOnly);
                var m = fldr.GetMessage(new UniqueId(uid));
                client.Disconnect(true);

                StringBuilder sb = new StringBuilder();
                sb.Append("From: ");
                sb.AppendLine(m.From[0].ToString().Replace("\"", "").Replace("<", "").Replace(">", "").Trim());
                sb.Append("To: ");
                sb.AppendLine(mailAccount);
                sb.Append("Subject: ");
                sb.AppendLine(m.Subject);
                sb.AppendLine();
                sb.AppendLine();
                sb.AppendLine(m.GetTextBody(MimeKit.Text.TextFormat.Plain));
                rfMessagePreview preview = new rfMessagePreview();
                preview.id = uid;
                preview.preview = sb.ToString();
                preview.isKeyRequest = m.Subject.StartsWith("Request for 30 day temporary");
                return preview;
            }
        }//get message

        public class rfMessagePreview
        {
            public bool isKeyRequest;
            public string preview;
            public uint id;
        }



        /// <summary>
        /// 
        /// </summary>
        /// <param name="mailAccount"></param>
        /// <param name="mailFolder"></param>
        /// <param name="mirrorServer"></param>
        /// <returns></returns>
        public static IMessageSummary GetMostRecentMessageSummary(string mailAccount, string mailFolder, bool mirrorServer)
        {

            using (var client = new ImapClient())
            {
                // Accept all SSL certificates
                client.ServerCertificateValidationCallback = (s, c, h, e) => true;
                //client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT, true);
                if (mirrorServer)
                    client.Connect(MAIL_MIRROR_IMAP_ADDRESS, MAIL_IMAP_PORT);
                else
                    client.Connect(MAIL_IMAP_ADDRESS, MAIL_IMAP_PORT);
                // Note: since we don't have an OAuth2 token, disable
                // the XOAUTH2 authentication mechanism.
                client.AuthenticationMechanisms.Remove("XOAUTH2");

                if (mailAccount == "support@ayanova.com")
                {
                    client.Authenticate(MAIL_ACCOUNT_SUPPORT, MAIL_ACCOUNT_PASSWORD_SUPPORT);
                }
                if (mailAccount == "sales@ayanova.com")
                {
                    client.Authenticate(MAIL_ACCOUNT_SALES, MAIL_ACCOUNT_PASSWORD_SALES);
                }

                var fldr = client.GetFolder(mailFolder);
                fldr.Open(FolderAccess.ReadOnly);
                //---------------
                int Count = fldr.Count;
                var messages = fldr.Fetch(Count - 1, Count, MessageSummaryItems.Envelope).ToList();

                //--------------
                client.Disconnect(true);
                if (messages.Count > 0)
                    return messages[0];
                else
                    return null;

            }
        }//get message


        /////////////////////////////////////////////////////////////////
        //Validate mirroring is working
        //
        //
        public static bool MailIsMirroringProperly()
        {
            //check that the mirror and master contain the same most recent message in 
            //Sent
            //AyaNovaReceivedSubscribed
            //

            var SentMasterSummary = GetMostRecentMessageSummary("support@ayanova.com", "Sent", false);
            var AyaNovaReceivedSubscribedSummary = GetMostRecentMessageSummary("support@ayanova.com", "AyaNovaReceivedSubscribed", false);
            var SentMirrorSummary = GetMostRecentMessageSummary("support@ayanova.com", "Sent", true);
            var AyaNovaReceivedSubscribedMirrorSummary = GetMostRecentMessageSummary("support@ayanova.com", "AyaNovaReceivedSubscribed", true);

            return SentMasterSummary.UniqueId.Id == SentMirrorSummary.UniqueId.Id && AyaNovaReceivedSubscribedSummary.UniqueId.Id == AyaNovaReceivedSubscribedMirrorSummary.UniqueId.Id;


        }


        //----------------
    }//eoc
}//eons
