using System.Net;
using System.Net.Mail;
using System.Text.RegularExpressions;

namespace Fast.Logic;

public static partial class Util
{
    public class Email
    {
        //this is a google account "app" credential
        private static readonly NetworkCredential devCredentials = new("joshlupo@gmail.com", "zjnz srxu qeqt tnzy");
        private static readonly char[] separators = [',', ';', ' '];
        private readonly EmailSetting emailSettings;
        private readonly FaxSetting faxSettings;
        private readonly Notification notification;
        private readonly MyDbContext db;

        public Email(MyDbContext db, Enums.NotificationType notifyType)
        {
            this.db = db;
            notification = GetNotification(notifyType);
            emailSettings = GetEmailSettings();
            faxSettings = GetFaxSettings();
        }

        public Email(MyDbContext db, string fromAddress, string fromName = "")
        {
            var customNotification = new Notification();
            customNotification.FromAddress = fromAddress;
            customNotification.FromName = fromName;
            notification = customNotification;
            this.db = db;
            emailSettings = GetEmailSettings();
            faxSettings = GetFaxSettings();
        }

        private EmailSetting GetEmailSettings()
        {
            var emailSettings = db.EmailSettings.First();
            return emailSettings;
        }

        private FaxSetting GetFaxSettings()
        {
            var faxSettings = new FaxSetting();
            faxSettings.Provider = db.AppSettings.Where(x => x.Name == "EmailToFaxProviderDomainName").Select(x => x.Value).First();
            faxSettings.OutsideLineCode = db.AppSettings.Where(x => x.Name == "OutsideLineCode").Select(x => x.Value).First();
            faxSettings.LocalAreaCodes = db.AppSettings.Where(x => x.Name == "LocalAreaCodes").Select(x => x.Value).ToList();
            return faxSettings;
        }

        private Notification GetNotification(Enums.NotificationType notifyType)
        {
            var notification = db.Notifications.Where(x => x.Id == (int)notifyType).First();
            return notification;
        }

        public static void ValidateEmail(string email)
        {
            email = email.Trim();
            if (string.IsNullOrWhiteSpace(email))
                throw new Exception("Empty email address");

            if (email.EndsWith('.'))
                throw new Exception($"Email address cannot end with a period: {email}");

            var isValidDotNetEmail = new System.ComponentModel.DataAnnotations.EmailAddressAttribute().IsValid(email);
            if (!isValidDotNetEmail)
                throw new Exception($"Invalid email address: {email}");
        }

        private static void AddToAddresses(string toAddresses, MailMessage mailMessage)
        {
            //split toAddresses into individual addresses where they may be separated by any combination of commas, semicolons, or spaces
            var splitAddresses = toAddresses.Split(separators, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
            foreach (var toAddress in splitAddresses)
            {
                ValidateEmail(toAddress);
                mailMessage.To.Add(new MailAddress(toAddress));
            }
        }

        public string SendEmail(string? toAddresses, string subject, string body, Attachment? attachment)
        {
            var client = new SmtpClient
            {
                Host = emailSettings.SmtpServer,
                Port = emailSettings.Port,
                Timeout = 30000,
                UseDefaultCredentials = !emailSettings.UseAuthentication,
                EnableSsl = emailSettings.ConnectionType == 3
            };
            if (!client.UseDefaultCredentials)
                client.Credentials = new NetworkCredential(emailSettings.UserName, emailSettings.Password);

            if (string.IsNullOrWhiteSpace(notification.FromAddress))
                throw new Exception("FromAddress is required for sending email");

            var mailMessage = new MailMessage
            {
                From = new MailAddress(notification.FromAddress, notification.FromName),
                Subject = subject,
                IsBodyHtml = emailSettings.UseHtml,
                Body = body
            };
            if (attachment != null)
                mailMessage.Attachments.Add(attachment);

            if (Main.IsDevOrTest)
            {
                client.Host = "smtp.gmail.com";
                client.Port = 587;
                client.Credentials = devCredentials;
                client.EnableSsl = true;
                AddToAddresses("joshlupo@gmail.com,joshlupo@protonmail.com", mailMessage);
            }
            else
            {
                if (!string.IsNullOrWhiteSpace(toAddresses))
                    AddToAddresses(toAddresses, mailMessage);
                if (!string.IsNullOrWhiteSpace(notification.ToAddresses))
                    AddToAddresses(notification.ToAddresses, mailMessage);
            }

            //mailMessage.ReplyToList.Add(notification.ReplyToAddress ?? "");

            client.Send(mailMessage);

            string sentToAddresses = string.Join(",", mailMessage.To.Select(x => x.Address));
            return sentToAddresses;
        }

        public string SendFaxViaEmail(string faxNumber, string recipient, string subject, Attachment attachment)
        {
            var client = new SmtpClient
            {
                Host = emailSettings.SmtpServer,
                Port = emailSettings.Port,
                Timeout = 30000,
                UseDefaultCredentials = !emailSettings.UseAuthentication,
                EnableSsl = emailSettings.ConnectionType == 3
            };
            if (!client.UseDefaultCredentials)
                client.Credentials = new NetworkCredential(emailSettings.UserName, emailSettings.Password);

            if (string.IsNullOrWhiteSpace(notification.FromAddress))
                throw new Exception("FromAddress is required for sending email");

            var mailMessage = new MailMessage
            {
                From = new MailAddress(notification.FromAddress, notification.FromName),
                Subject = subject,
                IsBodyHtml = false,
                Body = string.Empty
            };
            if (attachment != null)
                mailMessage.Attachments.Add(attachment);

            if (Main.IsDevOrTest)
            {
                client.Host = "smtp.gmail.com";
                client.Port = 587;
                client.Credentials = devCredentials;
                client.EnableSsl = true;
                string faxNumberNormalized = GetFaxNumberNormalized("8327537482");
                string toAddress = $"{faxNumberNormalized}@metrofax.com";
                mailMessage.To.Add(new MailAddress(toAddress, recipient));
            }
            else
            {
                string faxNumberNormalized = GetFaxNumberNormalized(faxNumber);
                string toAddress = $"{faxNumberNormalized}@{faxSettings.Provider}";
                mailMessage.To.Add(new MailAddress(toAddress, recipient));
            }

            client.Send(mailMessage);

            string sentToAddresses = string.Join(",", mailMessage.To.Select(x => x.Address));
            return sentToAddresses;
        }

        private static string GetFaxNumberNormalized(string faxNumber)
        {
            faxNumber = faxNumber.Trim();
            //use regex to split the input into country code, area code, and number
            string pattern = @"^(?:(?:[\+]?(?<CountryCode>[\d]{1,3}(?:[ ]+|[\-.])))?[(]?(?<AreaCode>[\d]{3})[\-/)]?(?:[ ]+)?)?(?<Number>[a-zA-Z2-9][a-zA-Z0-9 \-.]{6,})(?:(?:[ ]+|[xX]|(i:ext[\.]?)){1,2}(?<Ext>[\d]{1,5}))?$";
            Match regexResult = Regex.Match(faxNumber, pattern);
            string countryCode = regexResult.Groups["CountryCode"].Value.Trim();
            countryCode = !string.IsNullOrWhiteSpace(countryCode) ? countryCode : "1";
            string areaCode = regexResult.Groups["AreaCode"].Value.Trim();
            string number = regexResult.Groups["Number"].Value.Trim();

            string result = countryCode + areaCode + number;
            result = result.Replace("-", "").RemoveWhitespace();
            return result;
        }
    }
}
