// Copyright © Selmo Technology GmbH. All Rights Reserved

using Novus.Shared.Types.Interval;
using SelmoStudio.Hmi.Server.ProcessValue.Services;
using SelmoStudio.Hmi.Server.Shared.Model;

namespace SelmoStudio.Hmi.Server.Shared.Service
{
    internal class HmiWatchdog : IHmiWatchdog
    {
        public event EventHandler<WatchdogEventArgs>? OnCycleSecondElapsed;

        public event EventHandler<WatchdogEventArgs>? OnCycleMinuteElapsed;

        public IntervalPolling<bool> _intervalPolling;

        public int BaseUpdateRateMs { get; private set; }

        public IProcessValuePollingService _processValuePollingService;
        private readonly ILogger<HmiWatchdog> _logger;

        private readonly DeviceConnectionCache _deviceConnectionCache;

        public HmiWatchdog(IProcessValuePollingService processValuePollingService, ILogger<HmiWatchdog> logger)
        {
            _intervalPolling = new IntervalPolling<bool>(new Func<PollingInfo, Task<bool>>(OnUpdate));
            _processValuePollingService = processValuePollingService;
            _logger = logger;
            _deviceConnectionCache = new();
        }

        public void Init(int baseUpdateRate = 1000)
        {
            BaseUpdateRateMs = baseUpdateRate;
            RefreshIntervalPolling();
        }

        private void RefreshIntervalPolling()
        {
            try
            {
                _intervalPolling.Stop();
                _intervalPolling.SetInterval(BaseUpdateRateMs);
                _intervalPolling.Start();
            }
            catch (Exception ex)
            {
                _logger.LogCritical(ex, $"Error in {nameof(RefreshIntervalPolling)}");
            }
        }

        private Task<bool> OnUpdate(PollingInfo info)
        {
            if (info.IntervalIsExceeded)
            {
                _logger.LogWarning($"Polling Interval exceeded!! '{info.IntervalExceededMs}[ms]'");
            }

            _secondCounter += BaseUpdateRateMs / 1000;

            UpdateAllConnectedDevices();
            RaiseCycleEvents();
            ResetCounterEveryHour();

            return Task.FromResult(true);
        }

        private void UpdateAllConnectedDevices()
        {
            DeviceConnectionInfo connInfo = new()
            {
                DeviceId = _processValuePollingService.DeviceId,
                IsConnectionActive = _processValuePollingService.IsConnectionActive(),
            };
            _deviceConnectionCache.UpdateConnectionStates(connInfo);
        }

        public int _secondCounter = 0;

        public const int SecondsPerHour = 3600;
        public const int SecondsPerMinute = 60;

        private void RaiseCycleEvents()
        {
            WatchdogEventArgs args = new WatchdogEventArgs()
            {
                ConnectedDevices = _deviceConnectionCache.DeviceConnections,
                ConnectionLostDevices = _deviceConnectionCache.DeviceConnectionsLost,
            };

            if (OnCycleSecondElapsed != null)
            {
                HandleConnectionLost(args);
                OnCycleSecondElapsed(this, args);
            }

            if (OnCycleMinuteElapsed != null &&
                _secondCounter % SecondsPerMinute == 0)
            {
                OnCycleMinuteElapsed(this, args);
            }
        }

        private void ResetCounterEveryHour()
        {
            if (_secondCounter % SecondsPerHour == 0)
            {
                _secondCounter = 0;
            }
        }

        private int _counter;
        private const int ReconnectTicks = 30;

        private void HandleConnectionLost(WatchdogEventArgs args)
        {
            _counter++;
            foreach (var connectedLostDevice in args.ConnectionLostDevices)
            {
                if (_processValuePollingService.DeviceId.Equals(connectedLostDevice))
                {
                    if (_counter % ReconnectTicks == 0)
                    {
                        //_processValuePollingService.Reconnect();
                        _counter = 0;
                    }
                }
            }
        }
    }
}