using System;

namespace StateHasChanged2.SignalR
{
    using Microsoft.AspNetCore.SignalR;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Threading.Tasks;

    public class SignalRHub : Hub
    {
        #region Classi
        public class SessionInfo
        {
            #region Proprietà
            /// <summary>
            /// Id dell'utente
            /// </summary>
            public Guid UserId { get; set; }
            /// <summary>
            /// Id della sessione
            /// </summary>
            public Guid SessionId { get; set; }
            #endregion

            #region Costruttore
            /// <summary>
            /// Costruttore
            /// </summary>
            /// <param name="userId">Id dell'utente</param>
            /// <param name="sessionId">Id della sessione</param>
            public SessionInfo(Guid userId, Guid sessionId)
            {
                UserId = userId;
                SessionId = sessionId;
            }
            #endregion
        }
        #endregion

        #region Membri privati
        private static IHubContext<SignalRHub> m_hubContext;
        #endregion

        #region Proprietà
        /// <summary>
        /// Elenco delle connessioni signalR attive
        /// </summary>
        public static ConcurrentDictionary<string, SessionInfo> Connections = new ConcurrentDictionary<string, SessionInfo>();
        #endregion

        #region Costruttore
        /// <summary>
        /// Costruttore
        /// </summary>
        public SignalRHub(IHubContext<SignalRHub> hubContext) : base()
        {
            m_hubContext = hubContext;
        }
        #endregion

        #region Metodi privati
        /// <summary>
        /// Restituisce l'id della connessione signalR associato alla sessione utente
        /// </summary>
        /// <param name="sessionId">Id della sessione utente</param>
        /// <returns>Id della connessione signalR associato alla sessione utente</returns>
        private static string GetSessionConnectionId(Guid sessionId)
        {
            foreach (var kvp in Connections)
            {
                var connectionId = kvp.Key;
                var sessionInfo = kvp.Value;

                if (sessionInfo?.SessionId == sessionId)
                    return connectionId;
            }

            return "";
        }
        /// <summary>
        /// Restituisce l'elenco degli id delle connessioni signalR associate all'utente
        /// </summary>
        /// <param name="userId">Id dell'utente</param>
        /// <returns>Elenco degli id delle connessioni signalR associate all'utente</returns>
        private static List<string> GetUserConnections(Guid userId)
        {
            var result = new List<string>();

            foreach (var kvp in Connections)
            {
                var connectionId = kvp.Key;
                var sessionInfo = kvp.Value;

                if (sessionInfo.UserId == userId)
                    result.Add(connectionId);
            }

            return result;
        }
        /// <summary>
        /// Restituisce l'elenco degli id delle connessioni signalR associate all'utente
        /// </summary>
        /// <returns>Elenco degli id delle connessioni signalR associate all'utente</returns>
        private static List<string> GetUserConnections()
        {
            var result = new List<string>();

            foreach (var kvp in Connections)
            {
                var connectionId = kvp.Key;
                var sessionInfo = kvp.Value;

                result.Add(connectionId);
            }

            return result;
        }
        #endregion

        #region Metodi pubblici
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }

        #region Gestione connessione / disconnessione
        /// <summary>
        /// Notifica ai client la connessione di un nuovo client
        /// </summary>
        /// <returns>Task dell'operazione asincrona</returns>
        public override Task OnConnectedAsync()
        {
            Connections.TryAdd(Context.ConnectionId, null);

            Clients.Caller.SendAsync("Connected", Context.ConnectionId);

            return base.OnConnectedAsync();
        }
        /// <summary>
        /// Notifica ai client la disconnessione di un client
        /// </summary>
        /// <param name="stopCalled"></param>
        /// <returns>Task dell'operazione asincrona</returns>
        public override Task OnDisconnectedAsync(Exception exception)
        {
            Connections.TryRemove(Context.ConnectionId, out _);

            Clients.Caller.SendAsync("Disconnected", Context.ConnectionId);

            return base.OnDisconnectedAsync(exception);
        }
        /// <summary>
        /// Imposta la connessione nell'elenco delle connessioni attive
        /// </summary>
        /// <param name="connectionId">Id della connessione</param>
        /// <param name="userId">Id dell'utente</param>
        /// <param name="sessionId">Id della sessione</param>
        public static void SetConnection(string connectionId, Guid userId, Guid sessionId)
        {
            Connections[connectionId] = new SessionInfo(userId, sessionId);
        }
        /// <summary>
        /// Elimina la connessione SignalR dall'elenco delle connessioni attive
        /// </summary>
        /// <param name="connectionId">Id della connessione SignalR</param>
        public static void RemoveConnection(string connectionId)
        {
            Connections.TryRemove(connectionId, out _);
        }
        #endregion

        /// <summary>
        /// Gestisce la notifica di avanzamento
        /// </summary>
        /// <param name="sessionId">Id della sessione</param>
        /// <param name="e">Informazioni sul processo di avanzamento</param>
        public static void NotifyProgress(Utils.Utils.ProgressEventArgs e)
        {
            if (m_hubContext == null)
                return;

            var connections = GetUserConnections();

            foreach (var connectionId in connections)
                m_hubContext.Clients.Client(connectionId).SendAsync("Progress", e);
        }
        #endregion
    }
}
