// Copyright © Selmo Technology GmbH. All Rights Reserved

using SelmoStudio.Hmi.Server.SelmoHmi.Notification.Factory;
using SelmoStudio.Hmi.Server.SelmoHmi.Notification.Model;
using System.Timers;

namespace SelmoStudio.Hmi.Server.SelmoHmi.Notification.Service
{
    public class ProgressService : IProgressService, IDisposable
    {
        private INotificationService _notificationService;

        private const int TimerInterval = 500;

        private Dictionary<string, ProgressNotificationTask> _progressNotificationTasks;

        public ProgressService(INotificationService notificationService)
        {
            _notificationService = notificationService;
            _progressNotificationTasks = new();
        }

        public async Task RunWithProgressAsync(Func<Task> function)
        {
            var notificationConfiguration = NotificationConfigurationFactory.CreateProgress();
            var timer = GetTimer(notificationConfiguration.Id.ToString());
            var progressNotificationTasks = new ProgressNotificationTask(timer, notificationConfiguration,
                function);

            _progressNotificationTasks.Add(notificationConfiguration.Id.ToString(), progressNotificationTasks);

            await InvokeCallBackAsync(progressNotificationTasks);
        }

        private async Task InvokeCallBackAsync(ProgressNotificationTask progressNotificationTask)
        {
            try
            {
                StartInProgressNotificationTimer(progressNotificationTask.Timer);
                await progressNotificationTask.Function();
            }
            catch (Exception ex)
            {
                progressNotificationTask.ErrorOccurred = true;
            }
            finally
            {
                OnCallBackFinished(progressNotificationTask);
            }
        }

        private void OnCallBackFinished(ProgressNotificationTask progressNotificationTask)
        {
            TearDownTimer(progressNotificationTask.Timer);

            CloseInProgressNotification(progressNotificationTask.NotificationConfiguration.Id.ToString());

            if (progressNotificationTask.ErrorOccurred)
            {
                ShowErrorMessage();
            }

            if (_progressNotificationTasks.TryGetValue(progressNotificationTask.NotificationConfiguration.Id.ToString(), out _))
            {
                _progressNotificationTasks.Remove(progressNotificationTask.NotificationConfiguration.Id.ToString());
            }
        }

        private ProgresNotificationTimer GetTimer(string notificationConfigurationId)
        {
            var timer = new ProgresNotificationTimer(TimerInterval, notificationConfigurationId);
            timer.Elapsed += OnTimerElapsed;

            return timer;
        }

        private void TearDownTimer(ProgresNotificationTimer timer)
        {
            StopInProgressNotificationTimer(timer);

            timer.Elapsed -= OnTimerElapsed;
            timer.Dispose();
        }

        private void StopInProgressNotificationTimer(ProgresNotificationTimer timer)
        {
            if (timer.Enabled)
            {
                timer.Stop();
            }
        }

        private void StartInProgressNotificationTimer(ProgresNotificationTimer timer)
        {
            if (timer.Enabled)
            {
                return;
            }

            timer.Start();
        }

        private void CloseInProgressNotification(string notificationConfigurationId)
        {
            _notificationService.RemoveNotificationIfExists(notificationConfigurationId);
        }

        private void OnTimerElapsed(object? source, ElapsedEventArgs args)
        {
            if (source is ProgresNotificationTimer progressNotificationTimer)
            {
                StopInProgressNotificationTimer(progressNotificationTimer);

                if (_progressNotificationTasks.TryGetValue(progressNotificationTimer.Id, out var progressNotificationTask))
                {
                    _notificationService.Notify(progressNotificationTask.NotificationConfiguration);
                }
            }
        }

        private void ShowErrorMessage()
        {
            _notificationService.Notify(NotificationConfigurationFactory.CreateError());
        }

        public void Dispose()
        {
        }
    }
}