// Copyright © Selmo Technology GmbH. All Rights Reserved

using Microsoft.AspNetCore.Components.Web;
using Novus.Connectivity.Common;
using Novus.Connectivity.Plc;
using Novus.Connectivity.Service;
using Novus.Shared.Types.Interval;
using Novus.Shared.Types.Nothing;
using Novus.Shared.Types.Result;
using SelmoStudio.Hmi.Server.Diagnostic;
using SelmoStudio.Hmi.Server.SelmoHmi.Shared.Component;
using System.Collections.Immutable;

namespace SelmoStudio.Hmi.Server.ProcessValue.Services
{
    internal class ProcessValuePollingService : IProcessValuePollingService
    {
        public event EventHandler OnIntervalElapsed;

        public IntervalPolling<bool> _intervalPolling;
        private IConnectionSettings _connectionSettings;
        private IRegisteredValueConnectionService _regValueService;
        private readonly IAppDiagnosticService _appDiagnosticService;
        private readonly ILogger<ProcessValuePollingService> _logger;

        public DeviceId DeviceId { get; private set; }

        public DateTimeOffset LastValueChange => _regValueService.LastValueChange;

        public ProcessValuePollingService(IAppDiagnosticService appDiagnosticService, ILogger<ProcessValuePollingService> logger)
        {
            _appDiagnosticService = appDiagnosticService;
            _logger = logger;
        }

        public void Init(IConnectionSettings settings)
        {
            var connectionService = ConnectionServiceFactory.CreateRegistered(settings);
            Init(settings, connectionService);
            DeviceId = settings.DeviceId;
            RefreshIntervalPolling();
        }

        internal void Init(IConnectionSettings settings, IRegisteredValueConnectionService connectionService)
        {
            _appDiagnosticService.Init();
            Setup(settings, connectionService);
            DeviceId = settings.DeviceId;
            RefreshIntervalPolling();
        }

        public void Reconnect()
        {
            if (_regValueService != null)
            {
                _regValueService.Reconnect();
            }
        }

        public void Setup(IConnectionSettings settings)
        {
            _connectionSettings = settings;
            Setup(settings, ConnectionServiceFactory.CreateRegistered(settings));
        }

        public void Setup(IConnectionSettings settings, IRegisteredValueConnectionService connectionService)
        {
            _connectionSettings = settings;
            _regValueService = connectionService;
        }

        private bool RefreshIntervalPolling()
        {
            try
            {
                if (_intervalPolling == null)
                {
                    _intervalPolling = new IntervalPolling<bool>(new Func<PollingInfo, Task<bool>>(OnUpdate));
                }

                _intervalPolling.Stop();
                _intervalPolling.SetInterval(_connectionSettings.UpdateRate);
                _intervalPolling.Start();
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error in {nameof(RefreshIntervalPolling)}", ex);
            }

            return true;
        }

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

            if (OnIntervalElapsed != null)
            {
                ProgressEventArgs args = new ProgressEventArgs();
                OnIntervalElapsed(this, args);
            }

            return Task.FromResult(true);
        }

        private int _counterRegistrations = 0;

        public void RegisterValues(ValuesConnectionRegistration registration)
        {
            try
            {
                _logger.LogInformation($"{registration.Id} registration started, connection: {_regValueService.IsConnectionActive()}");

                _counterRegistrations++;
                _intervalPolling.Stop();

                Task task = Task.Factory.StartNew(() =>
                {
                    try
                    {
                        _regValueService.RegisterValues(registration);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError($"Error in {nameof(RegisterValues)} {ex.Message.ToString()}");
                    }
                });

                task.Wait(TimeSpan.FromSeconds(5));

                if (task.IsCompleted)
                {
                    _logger.LogInformation($"{registration.Id} registration finished, connection: {_regValueService.IsConnectionActive()}");
                }
                else
                {
                    _logger.LogError($"Timeout {nameof(RegisterValues)}: Reg {_counterRegistrations} : UnReg {_counterUnRegister} :  {registration.Id} : {_regValueService.IsConnectionActive()}");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error in {nameof(RegisterValues)} {ex.Message.ToString()}");
            }
            finally
            {
                _counterRegistrations--;
                RefreshIntervalPolling();
            }
        }

        private int _counterUnRegister = 0;

        public void UnRegisterValues(ValuesConnectionRegistrationId id)
        {
        }

        public async Task<OperationResult<ValueInfo>> GetValues(ValuesConnectionRegistrationId id)
        {
            ValueInfo empty = new()
            {
                LastUpdate = DateTimeOffset.MinValue,
                ProcessValues = ImmutableDictionary<ProcessValueId, IProcessValue>.Empty
            };
            OperationResult<ValueInfo> result = OperationResult<ValueInfo>.Create(empty);

            try
            {
                var hasRegistration = _regValueService.HasRegistration(id);

                if (hasRegistration && _regValueService.IsReady() && _regValueService.IsConnectionActive())
                {
                    result = await _regValueService.GetValues(id);
                    await _appDiagnosticService.AnalyzeRead(_regValueService, id);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"Error in {nameof(GetValues)} : {ex.Message}", new[] { ex });
            }
            return result;
        }

        public async Task<OperationResult<ValueInfo>> GetAllValues()
        {
            await _appDiagnosticService.AnalyzeReadAll(_regValueService);
            return await _regValueService.GetAllValues();
        }

        public async Task<OperationResult<Nothing>> WriteValue(IProcessValue value)
        {
            var result = await _regValueService.WriteValue(value);

            await _appDiagnosticService.AnalyzeWrite(_regValueService, value);
            return result;
        }

        public bool IsReady()
        {
            return _regValueService.IsReady();
        }

        public bool IsConnectionActive()
        {
            try
            {
                return _regValueService.IsConnectionActive();
            }
            catch
            {
                return false;
            }
        }

        //TODO
        public void HandleEvent(ComponentEventArgs e)
        {
        }
    }
}