using Correo.PublishedLanguage.Commands;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using NBB.Messaging.Abstractions;
using NetworkResurrector.Api.Application.Services.Abstractions;
using NetworkResurrector.Api.Domain.Constants;
using NetworkResurrector.Api.Domain.Models.Notifications;
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace NetworkResurrector.Api.Application.Services
{
    internal class NotificationService : INotificationService
    {
        private readonly IMessageBusPublisher _messageBusPublisher;
        private readonly IConfiguration _configuration;
        private readonly ILogger<NotificationService> _logger;

        public NotificationService(IMessageBusPublisher messageBusPublisher, IConfiguration configuration, ILogger<NotificationService> logger)
        {
            _messageBusPublisher=messageBusPublisher;
            _configuration=configuration;
            _logger=logger;
        }

        private Notification GetNotification(NotificationType type, NotificationContext context)
        {
            var template = _configuration.GetSection($"Notifications:{type}").Get<NotificationTemplate>();
            var notification = new Notification()
            {
                To = template.To,
                Subject = ReplacePlaceholders(template.Subject, context),
                Body = ReplacePlaceholders(template.Body, context)
            };

            return notification;
        }

        private string ReplacePlaceholders(string text, NotificationContext context)
        {
            var regex = new Regex(@"\{[A-Z_:0-9]+\}");
            MatchCollection matches = regex.Matches(text);
            var distinctMatches = matches.OfType<Match>().Select(m => m.Value).Distinct();
            foreach (var placeholder in distinctMatches)
            {
                var placeholderValue = GetPlaceholderValue(placeholder, context);
                if (string.IsNullOrEmpty(placeholderValue))
                    _logger.LogWarning($"The placeholder '{placeholder}' was not recognized and was ignored.");
                else
                    text = text.Replace(placeholder, placeholderValue);
            }
            return text;
        }

        private string GetPlaceholderValue(string placeholder, NotificationContext context)
        {
            return placeholder switch
            {
                NotificationPlaceholder.MACHINE_NAME => context.MachineName,
                NotificationPlaceholder.MACHINE_FULLNAME => context.MachineFullName,
                NotificationPlaceholder.MACHINE_IP => context.MachineIP,
                NotificationPlaceholder.ACTION_STATUS => context.ActionStatus,
                NotificationPlaceholder.ACTION_PERFORMER => context.ActionPerformer,
                NotificationPlaceholder.SYSTEM_DATETIME => DateTime.Now.ToString(),
                NotificationPlaceholder.ERROR_MESSGE => context.ErrorMessage,
                NotificationPlaceholder.NEWLINE => Environment.NewLine,
                _ => GetRegexPlaceholderValue(placeholder),
            };
        }

        private string GetRegexPlaceholderValue(string placeholder)
        {
            var placeholderValue = GetNewLinesPlaceholderValue(placeholder);
            // check here for other regex placeholders
            return placeholderValue;
        }

        private string GetNewLinesPlaceholderValue(string placeholder)
        {
            var regex = new Regex(@"\{NEWLINE(?::\d+)?\}");
            var match = regex.Match(placeholder);
            if (!match.Success)
                return null;

            var linesCountRegex = new Regex(@"(?<=:)[0-9]+");
            var linesCountMatch = linesCountRegex.Match(placeholder);
            if (!linesCountMatch.Success)
                return null;

            var linesCount = Convert.ToInt32(linesCountMatch.Value);
            var placeHolderValue = string.Concat(Enumerable.Repeat(Environment.NewLine, linesCount));

            return placeHolderValue;
        }

        public async Task Notify(NotificationType type, NotificationContext context, CancellationToken cancellationToken = default)
        {
            var notification = GetNotification(type, context);
            var cmd = new SendEmail()
            {
                Subject = notification.Subject,
                Body = notification.Body,
                IsBodyHtml = false,
                To = notification.To.Select(z => new SendEmail.MailAddress(z))
            };

            await _messageBusPublisher.PublishAsync(cmd, cancellationToken);
        }

        public async Task NotifyError(string errorMessage, CancellationToken cancellationToken = default)
        {
            var context = new NotificationContext()
            {
                ErrorMessage = errorMessage
            };

            await Notify(NotificationType.Error, context, cancellationToken);
        }

        public async Task Notify(NotificationType type, string machineName, string machineFullName, string machineIP, string actionStatus, string actionPerformer, string errorMessage, CancellationToken cancellationToken = default)
        {
            var context = new NotificationContext()
            {
                MachineName = machineName,
                MachineFullName = machineFullName,
                MachineIP = machineIP,
                ActionStatus = actionStatus,
                ActionPerformer = actionPerformer,
                ErrorMessage= errorMessage,
            };

            await Notify(type, context, cancellationToken);
        }
    }
}
