using log4net;
using MailKit;
using MailKit.Net.Imap;
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
using Mono.Addins;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Security;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using System.Timers;

[assembly: Addin("MailKitMailModule", "0.1")]
[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
namespace OpenSim.Modules.EMail
{
    [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MailKitMailModule")]
    class MailKitMailModule : INonSharedRegionModule, IEmailModule
    {
        private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        private IConfigSource m_config = null;
        private bool m_enabled = false;

        private Scene m_scene = null;

        private Timer m_timer = null;

        private List<InternalMail> m_messages = new List<InternalMail>();

        private String SMTP_SERVER_HOSTNAME = null;
        private String SMTP_SERVER_LOGIN = null;
        private String SMTP_SERVER_PASSWORD = null;
        private String SMTP_SERVER_SENDER = null;
        private int SMTP_SERVER_PORT = 25;
        private bool SMTP_SERVER_SSL = false;
        private bool SMTP_SERVER_TLS = false;

        private String IMAP_SERVER_HOSTNAME = null;
        private String IMAP_SERVER_LOGIN = null;
        private String IMAP_SERVER_PASSWORD = null;
        private int IMAP_SERVER_PORT = 25;
        private bool IMAP_SERVER_SSL = false;
        private bool IMAP_SERVER_TLS = false;


        #region ISharedRegionModule
        public string Name
        {
            get { return "MailKitMailModule"; }
        }

        public Type ReplaceableInterface
        {
            get { return null; }
        }

        public void AddRegion(Scene scene)
        {
            if (!m_enabled)
                return;

            scene.RegisterModuleInterface<IEmailModule>(this);
            m_scene = scene;

            if(IMAP_SERVER_HOSTNAME != String.Empty)
            {
                m_timer = new Timer();
                m_timer.Interval = 1000 * 60;
                m_timer.Elapsed += checkForMails;
                m_timer.Enabled = true;
                m_timer.Start();
            }
        }

        public void Close()
        {
            throw new NotImplementedException();
        }

        public void Initialise(IConfigSource source)
        {
            m_config = source;

            if(source.Configs["Startup"] != null)
            {
                m_enabled = (m_config.Configs["Startup"].GetString("emailmodule", "DefaultEmailModule") == "MailKitMailModule");

                if (!m_enabled)
                    return;
            }

            if (source.Configs["Mail"] == null)
                return;

            SMTP_SERVER_HOSTNAME = m_config.Configs["Mail"].GetString("SMTP_SERVER_HOSTNAME", String.Empty);
            SMTP_SERVER_PORT = m_config.Configs["Mail"].GetInt("SMTP_SERVER_PORT", 25);
            SMTP_SERVER_SSL = m_config.Configs["Mail"].GetBoolean("SMTP_SERVER_SSL", false);
            SMTP_SERVER_TLS = m_config.Configs["Mail"].GetBoolean("SMTP_SERVER_TLS", false);
            SMTP_SERVER_LOGIN = m_config.Configs["Mail"].GetString("SMTP_SERVER_LOGIN", String.Empty);
            SMTP_SERVER_PASSWORD = m_config.Configs["Mail"].GetString("SMTP_SERVER_PASSWORD", String.Empty);
            SMTP_SERVER_SENDER = m_config.Configs["Mail"].GetString("SMTP_SERVER_SENDER", "");

            IMAP_SERVER_HOSTNAME = m_config.Configs["Mail"].GetString("IMAP_SERVER_HOSTNAME", String.Empty);
            IMAP_SERVER_PORT = m_config.Configs["Mail"].GetInt("IMAP_SERVER_PORT", 143);
            IMAP_SERVER_SSL = m_config.Configs["Mail"].GetBoolean("IMAP_SERVER_SSL", false);
            IMAP_SERVER_TLS = m_config.Configs["Mail"].GetBoolean("IMAP_SERVER_TLS", false);
            IMAP_SERVER_LOGIN = m_config.Configs["Mail"].GetString("IMAP_SERVER_LOGIN", String.Empty);
            IMAP_SERVER_PASSWORD = m_config.Configs["Mail"].GetString("IMAP_SERVER_PASSWORD", String.Empty);
        }

        public void PostInitialise()
        {
        }

        public void RegionLoaded(Scene scene)
        {
        }

        public void RemoveRegion(Scene scene)
        {
        }
        #endregion

        private void checkForMails(object sender, ElapsedEventArgs f)
        {
            try
            {
                m_log.Info("[" + Name + "] checkForMails");

                using (var client = new ImapClient())
                {
                    client.CheckCertificateRevocation = false;
                    client.Timeout = 5000;
                    client.ServerCertificateValidationCallback = (s, c, h, e) => true;

                    if (IMAP_SERVER_SSL == true)
                    {
                        m_log.Info("[" + Name + "] Connect SSL");
                        client.Connect(IMAP_SERVER_HOSTNAME, IMAP_SERVER_PORT, SecureSocketOptions.Auto);
                    }
                    else if (IMAP_SERVER_TLS == true)
                    {
                        m_log.Info("[" + Name + "] Connect TLS");
                        client.Connect(IMAP_SERVER_HOSTNAME, IMAP_SERVER_PORT, SecureSocketOptions.StartTlsWhenAvailable);
                    }
                    else
                    {
                        m_log.Info("[" + Name + "] Connect None");
                        client.Connect(IMAP_SERVER_HOSTNAME, IMAP_SERVER_PORT, SecureSocketOptions.None);
                    }

                    if (IMAP_SERVER_LOGIN != String.Empty && IMAP_SERVER_PASSWORD != String.Empty)
                    {
                        m_log.Info("[" + Name + "] Login with " + IMAP_SERVER_LOGIN + ";" + IMAP_SERVER_PASSWORD);
                        client.Authenticate(IMAP_SERVER_LOGIN, IMAP_SERVER_PASSWORD);
                    
                    }

                    IMailFolder IMAPInbox = client.Inbox;
                    IMAPInbox.Open(FolderAccess.ReadWrite);

                    m_log.Info("[" + Name + "] Found " + IMAPInbox.Count + " messages.");
                    for (int i = 0; i < IMAPInbox.Count; i++)
                    {
                        MimeMessage message = IMAPInbox.GetMessage(i);
                        foreach (MailboxAddress adress in message.To.Mailboxes)
                        {
                            try
                            {
                                m_log.Info("[" + Name + "] Message To: " + adress.Address);
                                m_log.Info("[" + Name + "] Objekt ID: " + adress.Address.Split('@')[0]);

                                String UUIDString = adress.Address.Split('@')[0].Trim();

                                if(isUUID(UUIDString))
                                {
                                    UUID objID = UUID.Parse(UUIDString);
                                    SceneObjectPart sceneObject = m_scene.GetSceneObjectPart(objID);

                                    if (sceneObject != null)
                                    {
                                        m_messages.Add(new InternalMail(message, objID));
                                        IMAPInbox.SetFlags(i, MessageFlags.Deleted, true);
                                        m_log.Info("[" + Name + "] Get Message for objekt " + sceneObject.Name + " (" + sceneObject.UUID + ")");
                                    }
                                }
                                else
                                {
                                    IMAPInbox.SetFlags(i, MessageFlags.Deleted, true);
                                }
                            }catch(Exception _innerEroor)
                            {
                                m_log.Error("[" + Name + "] " + _innerEroor.Message);
                                IMAPInbox.SetFlags(i, MessageFlags.Deleted, true);
                            }
                        }
                    }

                    IMAPInbox.Expunge();
                    client.Disconnect(true);
                }
            }catch(Exception _error)
            {
                m_log.Error("[" + Name + "] " + _error.Message);
            }
        }

        public void SendEmail(UUID objectID, string address, string subject, string body)
        {
            SceneObjectPart sceneObject = m_scene.GetSceneObjectPart(objectID);

            MimeMessage message = new MimeMessage();
            message.From.Add(new MailboxAddress(sceneObject.Name, sceneObject.UUID + "@" + SMTP_SERVER_SENDER));
            message.To.Add(new MailboxAddress("", address));
            message.Subject = subject;
            message.Body = new TextPart("plain"){Text = body};

            message.Headers.Add(new Header(Encoding.UTF8, "ObjectID", sceneObject.UUID.ToString()));
            message.Headers.Add(new Header(Encoding.UTF8, "AvatarID", sceneObject.OwnerID.ToString()));
            message.Headers.Add(new Header(Encoding.UTF8, "Location", m_scene.Name + "@" + sceneObject.GetWorldPosition().ToString()));

            using (var client = new SmtpClient())
            {
                client.CheckCertificateRevocation = false;
                
                if (SMTP_SERVER_SSL == true)
                {
                    client.Connect(SMTP_SERVER_HOSTNAME, SMTP_SERVER_PORT, SecureSocketOptions.Auto);
                }
                else if(SMTP_SERVER_TLS == true)
                {
                    client.Connect(SMTP_SERVER_HOSTNAME, SMTP_SERVER_PORT, SecureSocketOptions.StartTlsWhenAvailable);
                }
                else
                {
                    client.Connect(SMTP_SERVER_HOSTNAME, SMTP_SERVER_PORT, SecureSocketOptions.None);
                }

                if(SMTP_SERVER_LOGIN != String.Empty && SMTP_SERVER_PASSWORD != String.Empty)
                    client.Authenticate(SMTP_SERVER_LOGIN, SMTP_SERVER_PASSWORD);

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

        public Email GetNextEmail(UUID objectID, string sender, string subject)
        {
            SceneObjectPart sceneObject = m_scene.GetSceneObjectPart(objectID);
            m_log.Info("[" + Name + "] GetNextEmail: " + sceneObject.Name + " (" + sceneObject.UUID + ")");
                       
            List<InternalMail> messages = m_messages.FindAll(X => X.ID == objectID);
            if (messages.Count == 0)
                return null;

            Email lslMessage = new Email();
            lslMessage.time = messages[0].Mail.Date.ToUnixTimeSeconds().ToString();
            lslMessage.message = messages[0].Mail.Body.ToString();
            lslMessage.sender = messages[0].Mail.Sender.GetAddress(true);
            lslMessage.subject = messages[0].Mail.Subject;
            lslMessage.numLeft = messages.Count - 1;

            m_messages.Remove(messages[0]);

            return lslMessage;
        }

        public bool isUUID(string thing)
        {
            UUID test;
            return UUID.TryParse(thing, out test) ? true : false;
        }
    }
}
