using System.Net;
using InABox.Core;
using Microsoft.Exchange.WebServices.Data;

namespace InABox.Mail
{
    
    class ExchangeTraceListener : ITraceListener
    {

        public void Trace(string traceType, string traceMessage)
        {
            Logger.Send(LogType.Information, traceType, traceMessage);
        }
        
    }
    
    public class ExchangeMailer : CoreMailer<ExchangeMailFolder, ExchangeMailSummary, ExchangeMailMessage>
    {
        private ExchangeService? Service;

        private ExchangeVersion Version = ExchangeVersion.Exchange2007_SP1;

        public IEnumerable<string> Versions()
        {
            var results = new List<string>();
            var values = Enum.GetValues(typeof(ExchangeVersion)).Cast<ExchangeVersion>();
            foreach (var value in values)
                results.Add(value.ToString());
            return results;
        }


        protected override void Initialize()
        {
            ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;

            Version = Enum.GetValues(typeof(ExchangeVersion)).Cast<ExchangeVersion>().Min();
        }

        public override void Dispose()
        {
            if (Service != null)
                Service = null;
        }
        
        private static bool RedirectionUrlValidationCallback(string redirectionUrl)
        {
            // The default for the validation callback is to reject the URL.
            bool result = false;
            Uri redirectionUri = new Uri(redirectionUrl);
            // Validate the contents of the redirection URL. In this simple validation
            // callback, the redirection URL is considered valid if it is using HTTPS
            // to encrypt the authentication credentials. 
            if (redirectionUri.Scheme == "https")
            {
                result = true;
            }
            return result;
        }

        protected override bool DoConnect()
        {
            try
            {
                Service = new ExchangeService(Version);
                Service.Timeout = 10000;
                Service.TraceListener = new ExchangeTraceListener();
                Service.TraceFlags = TraceFlags.All;
                Service.TraceEnabled = true;
                if (String.IsNullOrWhiteSpace(MailboxDomain))
                {
                    if (String.IsNullOrWhiteSpace(MailboxUserName))
                        Service.UseDefaultCredentials = true;
                    else
                    {
                        Service.Credentials = new WebCredentials(MailboxUserName, MailboxPassword);
                        if (String.IsNullOrWhiteSpace(MailboxHost))
                            Service.AutodiscoverUrl(MailboxUserName,RedirectionUrlValidationCallback);
                        else
                            Service.Url = new Uri($"https://{MailboxHost}:{(MailboxPort == 0 ? 443 : MailboxPort)}/EWS/Exchange.asmx");
                        var folder = Folder.Bind(Service, WellKnownFolderName.Inbox).ConfigureAwait(true);
                    }
                }
                else
                {
                    Service.Credentials = new WebCredentials(MailboxUserName, MailboxPassword, MailboxDomain);
                    if (MailboxHost.IsNullOrWhiteSpace())
                    {
                        Service.AutodiscoverUrl(MailboxUserName, RedirectionUrlValidationCallback);
                    }
                    else
                    {
                        Service.Url = new Uri(string.Format("https://{0}:{1}/EWS/Exchange.asmx", MailboxHost, MailboxPort == 0 ? 443 : MailboxPort));
                    }
                }
                return true;
            }
            catch (Exception err)
            {
                CoreUtils.LogException("", err);
                Service = null;
                return false;
            }
        }

        protected override bool GetIsConnected()
        {
            return Service != null;
        }
        
        private ExchangeService GetService()
        {
            return Service ?? throw new Exception("Not Connected");
        }

        protected override bool DoSendMessage(ExchangeMailMessage message)
        {
            var result = false;
            if (IsConnected)
            {
                var email = new EmailMessage(Service);
                if (message.To != null)
                    email.ToRecipients.AddRange(message.To.Select(x => new EmailAddress(x)));

                if (message.CC != null)
                    email.CcRecipients.AddRange(message.CC.Select(x => new EmailAddress(x)));

                if (message.BCC != null)
                    email.BccRecipients.AddRange(message.BCC.Select(x => new EmailAddress(x)));

                email.Subject = message.Subject;
                email.Body = new MessageBody(BodyType.Text, message.Body);
                email.SendAndSaveCopy().ContinueWith(MessageSent);
                result = true;
            }

            return result;
        }

        private void MessageSent(System.Threading.Tasks.Task task)
        {
        }

        protected override ExchangeMailFolder DoFindFolder(ExchangeMailFolder parent, string name)
        {
            FindFoldersResults results = null;
            if (parent == null)
                results = GetService().FindFolders(WellKnownFolderName.Inbox, new FolderView(int.MaxValue)).Result;
            else
                results = GetService().FindFolders(parent.Folder.Id, new FolderView(int.MaxValue)).Result;
            var result = results.FirstOrDefault(x => x.DisplayName.Equals(name));
            return new ExchangeMailFolder(result);
        }

        protected override ExchangeMailFolder GetInbox()
        {
            var folder = Folder.Bind(Service, WellKnownFolderName.Inbox).Result;
            return new ExchangeMailFolder(folder);
        }

        protected override ExchangeMailFolder GetSentItems()
        {
            var folder = Folder.Bind(Service, WellKnownFolderName.SentItems).Result;
            return new ExchangeMailFolder(folder);
        }

        protected override ExchangeMailMessage DoGetMessage(ExchangeMailFolder folder, string id)
        {
            throw new NotImplementedException();
        }

        protected override IEnumerable<ExchangeMailMessage> DoGetMessages(ExchangeMailFolder folder)
        {
            var parent = folder != null ? folder.Folder : GetService().FindFolders(WellKnownFolderName.Inbox, new FolderView(1)).Result.FirstOrDefault();
            if (parent != null)
            {
                FindItemsResults<Item> items = parent.FindItems(new ItemView(int.MaxValue)).Result;
                var results = new List<ExchangeMailMessage>();
                foreach (var item in items)
                    try
                    {
                        var message = EmailMessage.Bind(Service, item.Id).Result;
                        results.Add(new ExchangeMailMessage(message));
                    }
                    catch (Exception e)
                    {
                        CoreUtils.LogException("", e);
                    }

                return results;
            }

            return null;
        }

        protected override IEnumerable<ExchangeMailSummary> DoListMessages(ExchangeMailFolder folder, int min = 0, int max = int.MaxValue)
        {
            throw new NotImplementedException();
        }

        protected override ExchangeMailMessage DoCreateMessage()
        {
            var email = new EmailMessage(Service);
            return new ExchangeMailMessage(email);
        }

        protected override bool DoMoveMessage(ExchangeMailMessage message, ExchangeMailFolder to)
        {
            if (message != null && to != null)
            {
                message.Message.Move(to.Folder.Id);
                return true;
            }

            return false;
        }

        protected override bool DoSaveDraft(ExchangeMailMessage message)
        {
            message.Message.Save(WellKnownFolderName.Drafts);
            return true;
        }
    }
}

// internal class ExchangeVersion
// {
// }