using GreenTree.Nachtragsmanagement.Core.Plugins;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using GreenTree.Nachtragsmanagement.Core.Domain.Misc;
using GreenTree.Nachtragsmanagement.Services.User;
using GreenTree.Nachtragsmanagement.Services.Configuration;
using GreenTree.Nachtragsmanagement.Services.Appendix;
using GreenTree.Nachtragsmanagement.Core.Domain.Appendix;
using System.Net.Mail;
using System.Net;
using System.Globalization;
using GreenTree.Nachtragsmanagement.Services.Deviation;
using GreenTree.Nachtragsmanagement.Core.Domain.Deviation;
using Quartz;
using GreenTree.Nachtragsmanagement.Services.Logging;

namespace GreenTree.Nachtragsmanagement.Web.Implementations
{
    public class DeviationNotificationPlugin : INotificationPlugin, IJob
    {
        #region Services

        private readonly IUserService _userService;
        private readonly IConfigurationService _configurationService;
        private readonly IDeviationService _deviationService;
        private readonly IServiceLogger _logger;

        #endregion

        #region Properties

        /// <summary>
        /// Id
        /// </summary>
        public Guid Id
        {
            get
            {
                return Guid.Parse("77D662E0-F621-4567-9030-2A106533FE06");
            }
        }

        /// <summary>
        /// System name
        /// </summary>
        public string SystemName
        {
            get
            {
                return "GreenTree.Nachtragsmanagement.DeviationNotificationPlugin";
            }
        }

        /// <summary>
        /// List of available notification jobs
        /// </summary>
        public List<NotificationJob> AvailableNotificationJobs
        {
            get
            {
                return new List<NotificationJob>
                {
                    new NotificationJob
                    (
                        Guid.Parse("144DFF97-4EE4-4B32-967C-C0375D133DCF"),
                        "GreenTree.Nachtragsmanagement.DeviationNotificationPlugin.ProcessDeviationReceipt",
                        "Eingangsdatum überprüfen",
                        "Erstellt automatisch Benachrichtigungen für Vertragsabweichungen, die 40 Tagen, bzw. 60 Tagen nach " +
                        "Einreichung noch keinem Nachtrag zugeordnet sind."
                    )
                };
            }
        }

        /// <summary>
        /// Displayed name
        /// </summary>
        public string Name
        {
            get
            {
                return "Vertragsabweichungsbenachrichtigung";
            }
        }

        /// <summary>
        /// Further description on how this plugin works
        /// </summary>
        public string Description
        {
            get
            {
                return
                    "Erstellt automatisch Benachrichtigungen für Vertragsabweichungen, die 40 Tagen (Stufe 1), bzw. 60 Tagen (Stufe 2)" +
                    "nach Einreichung noch keinem Nachtrag zugeordnet sind.";
            }
        }
        #endregion

        /// <summary>
        /// Initializes a new instance of the DeviationNotificationPlugin class
        /// </summary>
        public DeviationNotificationPlugin() { }

        /// <summary>
        /// Initializes a new instance of the DeviationNotificationPlugin class
        /// </summary>
        public DeviationNotificationPlugin(
            IUserService userService,
            IConfigurationService configurationService,
            IDeviationService deviationService,
            IServiceLogger logger)
        {
            _userService = userService;
            _configurationService = configurationService;
            _deviationService = deviationService;
            _logger = logger;
        }

        #region Quartz implmentation

        /// <summary>
        /// Executes the current job
        /// </summary>
        /// <param name="context"></param>
        public void Execute(IJobExecutionContext context)
        {
            if (!context.JobDetail.JobDataMap.ContainsKey("MailNotifications"))
                return;

            var mailNotifications = context.JobDetail.JobDataMap.Get("MailNotifications") as IEnumerable<MailNotification>;

            ProcessNotifications(mailNotifications);
        }

        #endregion

        #region Processing

        /// <summary>
        /// Process all mail notifications registered for that plugin
        /// </summary>
        /// <param name="mailNotifications">The notifications which shall be generated.</param>
        public void ProcessNotifications(IEnumerable<MailNotification> mailNotifications)
        {
            if (mailNotifications == null || (mailNotifications != null && !mailNotifications.Any())) return;

            _logger.Information(
                String.Format(
                    "Starte Verarbeitung Mail-Benachrichtigung in \"{0}\" für \"{1} ...",
                    SystemName, mailNotifications.First().NotificationJobSystemName));

            foreach (var notification in mailNotifications)
            {
                try
                {
                    switch (notification.NotificationJobSystemName)
                    {
                        case "GreenTree.Nachtragsmanagement.DeviationNotificationPlugin.ProcessDeviationReceipt":
                            {
                                ProcessDeviationReceiptNotification(notification);
                            }
                            break;
                        default:
                            continue;
                    }
                }
                catch (Exception ex)
                {
                    _logger.Error(
                        String.Format(
                            "Fehler bei Mail-Benachrichtigung in \"{0}\" für \"{1}", SystemName, notification.NotificationJobSystemName), ex);
                }
            }

            _logger.Information(
                String.Format(
                    "Verarbeitung Mail-Benachrichtigung in \"{0}\" für \"{1} erfolgreich abgeschlossen!",
                    SystemName, mailNotifications.First().NotificationJobSystemName));
        }

        /// <summary>
        /// Notifies the corresponding recipients about all deviations whose receipt date is older than N days in two different groups
        /// </summary>
        /// <param name="mailNotification">The notification which shall be generated.</param>
        private void ProcessDeviationReceiptNotification(MailNotification mailNotification)
        {
            var ageDaysLevel1 = _configurationService.TryGetConfigItemValue<int>(
                "GreenTree.Nachtragsmanagement.DeviationNotificationPlugin.ProcessDeviationReceipt.AgeDaysLevel1", 40);

            var ageDaysLevel2 = _configurationService.TryGetConfigItemValue<int>(
                "GreenTree.Nachtragsmanagement.DeviationNotificationPlugin.ProcessDeviationReceipt.AgeDaysLevel2", 60);

            var deviationsLevel1 = _deviationService.GetAllDeviations()
                .Where(d => !d.AppendixId.HasValue &&
                            (DateTime.Now - d.ReceiptDate).Value.Days >= ageDaysLevel1 &&
                            (DateTime.Now - d.ReceiptDate).Value.Days < ageDaysLevel2)
                .ToList();

            var deviationsLevel2 = _deviationService.GetAllDeviations()
                .Where(d => !d.AppendixId.HasValue &&
                            (DateTime.Now - d.ReceiptDate).Value.Days >= ageDaysLevel2)
                .ToList();

            if (deviationsLevel1.Any() || deviationsLevel2.Any())
            {
                var mailBody = GenerateDeviationReceiptMailBody(deviationsLevel1, deviationsLevel2);

                SendNotification(mailNotification, "Autom. Übersicht nicht zugewiesene Vertragsabweichungen", mailBody);
            }
        }

        #endregion

        #region Mail body generation

        /// <summary>
        /// Generates a mail body with a list of all deviations whose receipt date is older than N days in two different groups
        /// </summary>
        /// <param name="deviationsLevel1">All deviations matching the level 1 criteria.</param>
        /// <param name="deviationsLevel2">All deviations matching the level 2 criteria.</param>
        public string GenerateDeviationReceiptMailBody(IEnumerable<Deviation> deviationsLevel1, IEnumerable<Deviation> deviationsLevel2)
        {
            var ageDaysLevel1 = _configurationService.TryGetConfigItemValue<int>(
                "GreenTree.Nachtragsmanagement.DeviationNotificationPlugin.ProcessDeviationReceipt.AgeDaysLevel1", 40);

            var ageDaysLevel2 = _configurationService.TryGetConfigItemValue<int>(
                "GreenTree.Nachtragsmanagement.DeviationNotificationPlugin.ProcessDeviationReceipt.AgeDaysLevel2", 60);

            var template =
                "<html>" +
                "   <body>" +
                "       {0}" +
                "       {1}" +
                "   </body>" +
                "</html>";

            var templateLevel1 =
                "<h3>Übersicht \"Offene Vertragsabweichungen älter als {0} Tage\"</h3>" +
                "<p>Folgende Vertragsabweichungen haben ein Einreichdatum älter als {0} Tage, sind aber noch keinen Nachtrag zugeordnet:</p>" +
                "<ul>" +
                "   {1}" +
                "</ul>";

            var templateLevel1List = String.Empty;

            if (deviationsLevel1.Any())
            {
                foreach (var deviation in deviationsLevel1)
                {
                    templateLevel1List += String.Format(
                        "<li>Vertragsabweichung <b>\"{0}\"</b> " +
                        "in Baustelle <b>\"{1}\"</b> - " +
                        "Einreichdatum: <b>{2:dd.MM.yyyy} ({3} Tage)</b>",
                        deviation.CustomNumber, 
                        deviation.Site == null
                            ? String.Empty
                            : deviation.Site.CustomNumber, 
                        deviation.ReceiptDate,
                        (DateTime.Now - deviation.ReceiptDate).Value.Days);
                }
            }

            var resultLevel1List = String.Format(templateLevel1, ageDaysLevel1, templateLevel1List);

            var templateLevel2 =
                "<h3>Übersicht \"Offene Vertragsabweichungen älter als {0} Tage\"</h3>" +
                "<p>Folgende Vertragsabweichungen haben ein Einreichdatum älter als {0} Tage, sind aber noch keinen Nachtrag zugeordnet:</p>" +
                "<ul>" +
                "   {1}" +
                "</ul>";

            var templateLevel2List = String.Empty;

            if (deviationsLevel2.Any())
            {
                foreach (var deviation in deviationsLevel2)
                {
                    templateLevel2List += String.Format(
                        "<li>Vertragsabweichung <b>\"{0}\"</b> " +
                        "in Baustelle <b>\"{1}\"</b> - " +
                        "Einreichdatum: <b>{2:dd.MM.yyyy} ({3} Tage)</b>",
                        deviation.CustomNumber,
                        deviation.Site == null
                            ? String.Empty
                            : deviation.Site.CustomNumber,
                        deviation.ReceiptDate,
                        (DateTime.Now - deviation.ReceiptDate).Value.Days);
                }
            }

            var resultLevel2List = String.Format(templateLevel2, ageDaysLevel2, templateLevel2List);

            return String.Format(template, resultLevel1List, resultLevel2List);
        }

        #endregion

        #region Mail sending

        /// <summary>
        /// Sends a generated mail body to the specified recipients in the mail notification
        /// </summary>
        /// <param name="mailNotification">The mail notification.</param>
        /// <param name="subject">The mail subject.</param>
        /// <param name="body">The mail body.</param>
        public void SendNotification(MailNotification mailNotification, string subject, string body)
        {
            if (mailNotification == null) return;

            var mailServerConfig = _configurationService.GetCurrentConfiguration().MailServerElement;

            var smptClient = new SmtpClient(mailServerConfig.SmtpServer, mailServerConfig.Port)
            {
                EnableSsl = mailServerConfig.UseSsl,
                Credentials = new NetworkCredential(
                    mailServerConfig.Username,
                    mailServerConfig.Password,
                    mailServerConfig.Domain)
            };

            var recipients =
                mailNotification.Users
                    .Select(u => u.MailAddress);

            var mailMessage = new MailMessage
            {
                IsBodyHtml = true,
                Subject = subject,
                Body = body,
                From = new MailAddress("Nachtragsbenachrichtigung@schweerbau.de")
            };

            foreach (var recipient in recipients)
                mailMessage.To.Add(recipient);

            smptClient.Send(mailMessage);
        }

        #endregion
    }
}