using C4IT.FASD.Base;
using C4IT.FASD.Cockpit.Communication;
using C4IT.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using static C4IT.Logging.cLogManager;

namespace FasdDesktopUi.Basics.Helper
{
    public class cDirectConnectionHelper
    {
        #region Properties and Fields

        private int? isTryingToConnectToDevice = null;

        private readonly cSupportCaseDataProvider dataProvider;

        private Guid directConnectionId = Guid.Empty;

        private readonly System.Timers.Timer directConnectionRenewTimer = new System.Timers.Timer();

        public bool IsDirectConnectionActive { get => directConnectionId.Equals(Guid.Empty) is false; }

        #endregion

        public event EventHandler DirectConnectionChanged;

        public cDirectConnectionHelper(cSupportCaseDataProvider dataProvider)
        {
            try
            {
                this.dataProvider = dataProvider;

                directConnectionRenewTimer.Interval = 1000 * 60 * 3;
                directConnectionRenewTimer.Elapsed += async (sender, e) => await DirectConnectionExtendDurationAsync(60 * 5);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public void Reset()
        {
            try
            {
                directConnectionId = Guid.Empty;
                isTryingToConnectToDevice = null;
                DirectConnectionChanged?.Invoke(this, EventArgs.Empty);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public async Task DirectConnectionStartAsync()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                const int maxRetries = 60;
                int retryCount = 0;

                bool shouldTryStartDirectConnection = true;
                while (shouldTryStartDirectConnection && retryCount < maxRetries)
                {
                    shouldTryStartDirectConnection = !(await CouldStartDirectConnectionAsync());

                    if (shouldTryStartDirectConnection)
                        await Task.Delay(1000 * 5);

                    retryCount++;
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        private async Task<bool> CouldStartDirectConnectionAsync()
        {
            try
            {
                if (dataProvider.Identities.Any(identity => identity.Class == enumFasdInformationClass.Computer) is false)
                {
                    LogEntry("Direct Connection - There was no information class of type computer found.", LogLevels.Error);
                    return true;
                }

                if (!dataProvider.NamedParameterEntries.TryGetValue("AgentDeviceId", out var agentDeviceIdParameter))
                {
                    LogEntry("Direct Connection - NamedParameter 'AgentDeviceId' could not be found.", LogLevels.Error);
                }
                if (agentDeviceIdParameter == null || !int.TryParse(agentDeviceIdParameter.GetValue(), out int agentDeviceId))
                {
                    LogEntry("Direct Connection - There was no valid AgentDeviceId found.", LogLevels.Error);
                    return true;
                }

                if (isTryingToConnectToDevice == agentDeviceId)
                    return true;

                isTryingToConnectToDevice = agentDeviceId;

                var tempConnectionId = await cFasdCockpitCommunicationBase.Instance.TryActivateDirectConnection(agentDeviceId);

                if (tempConnectionId is null)
                {
                    LogEntry("Direct Connection - Failed to receive connection status.", LogLevels.Error);
                    isTryingToConnectToDevice = null;
                    return false;
                }

                directConnectionId = tempConnectionId.Value;

                if (isTryingToConnectToDevice != agentDeviceId)
                    return true;

                isTryingToConnectToDevice = null;
                await DirectConnectionExtendDurationAsync(60 * 5);
                directConnectionRenewTimer.Start();
                DirectConnectionChanged?.Invoke(this, EventArgs.Empty);
                return true;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            
            return true;
        }

        public async Task DirectConnectionStopAsync()
        {

            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);

            try
            {
                directConnectionRenewTimer.Stop();

                if (directConnectionId.Equals(Guid.Empty))
                    return;

                await cFasdCockpitCommunicationBase.Instance.StopDirectConnection(directConnectionId);
                Reset();
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public async Task<bool> DirectConnectionExtendDurationAsync(int durationInSeconds)
        {
            try
            {
                if (directConnectionId.Equals(Guid.Empty))
                    return false;

                var output = await cFasdCockpitCommunicationBase.Instance.DirectConnectionExtendDuration(directConnectionId, durationInSeconds);

                if (output is false)
                    Reset();

                return output;
            }
            catch (Exception E)
            {
                LogException(E);
            }

            return false;
        }
    }
}
