using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Serilog;

namespace ZB.MOM.WW.LmxProxy.Host.Services
{
    /// <summary>
    ///     Manages client sessions for the gRPC service.
    ///     Tracks active sessions with unique session IDs.
    /// </summary>
    public class SessionManager : IDisposable
    {
        private static readonly ILogger Logger = Log.ForContext<SessionManager>();

        private readonly ConcurrentDictionary<string, SessionInfo> _sessions = new();
        private bool _disposed;

        /// <summary>
        ///     Gets the number of active sessions.
        /// </summary>
        public int ActiveSessionCount => _sessions.Count;

        /// <summary>
        ///     Creates a new session for a client.
        /// </summary>
        /// <param name="clientId">The client identifier.</param>
        /// <param name="apiKey">The API key used for authentication (optional).</param>
        /// <returns>The session ID for the new session.</returns>
        /// <exception cref="ObjectDisposedException">Thrown if the manager is disposed.</exception>
        public string CreateSession(string clientId, string apiKey = null)
        {
            if (_disposed)
            {
                throw new ObjectDisposedException(nameof(SessionManager));
            }

            var sessionId = Guid.NewGuid().ToString("N");
            var sessionInfo = new SessionInfo
            {
                SessionId = sessionId,
                ClientId = clientId ?? string.Empty,
                ApiKey = apiKey ?? string.Empty,
                ConnectedAt = DateTime.UtcNow,
                LastActivity = DateTime.UtcNow
            };

            _sessions[sessionId] = sessionInfo;

            Logger.Information("Created session {SessionId} for client {ClientId}", sessionId, clientId);

            return sessionId;
        }

        /// <summary>
        ///     Validates a session ID and updates the last activity timestamp.
        /// </summary>
        /// <param name="sessionId">The session ID to validate.</param>
        /// <returns>True if the session is valid; otherwise, false.</returns>
        public bool ValidateSession(string sessionId)
        {
            if (_disposed)
            {
                return false;
            }

            if (string.IsNullOrEmpty(sessionId))
            {
                return false;
            }

            if (_sessions.TryGetValue(sessionId, out SessionInfo sessionInfo))
            {
                sessionInfo.LastActivity = DateTime.UtcNow;
                return true;
            }

            return false;
        }

        /// <summary>
        ///     Gets the session information for a session ID.
        /// </summary>
        /// <param name="sessionId">The session ID.</param>
        /// <returns>The session information, or null if not found.</returns>
        public SessionInfo GetSession(string sessionId)
        {
            if (_disposed || string.IsNullOrEmpty(sessionId))
            {
                return null;
            }

            _sessions.TryGetValue(sessionId, out SessionInfo sessionInfo);
            return sessionInfo;
        }

        /// <summary>
        ///     Terminates a session.
        /// </summary>
        /// <param name="sessionId">The session ID to terminate.</param>
        /// <returns>True if the session was terminated; otherwise, false.</returns>
        public bool TerminateSession(string sessionId)
        {
            if (_disposed || string.IsNullOrEmpty(sessionId))
            {
                return false;
            }

            if (_sessions.TryRemove(sessionId, out SessionInfo sessionInfo))
            {
                Logger.Information("Terminated session {SessionId} for client {ClientId}", sessionId, sessionInfo.ClientId);
                return true;
            }

            return false;
        }

        /// <summary>
        ///     Gets all active sessions.
        /// </summary>
        /// <returns>A list of all active session information.</returns>
        public IReadOnlyList<SessionInfo> GetAllSessions()
        {
            return _sessions.Values.ToList();
        }

        /// <summary>
        ///     Disposes the session manager and clears all sessions.
        /// </summary>
        public void Dispose()
        {
            if (_disposed)
            {
                return;
            }

            _disposed = true;

            var count = _sessions.Count;
            _sessions.Clear();

            Logger.Information("SessionManager disposed, cleared {Count} sessions", count);
        }
    }

    /// <summary>
    ///     Contains information about a client session.
    /// </summary>
    public class SessionInfo
    {
        /// <summary>
        ///     Gets or sets the unique session identifier.
        /// </summary>
        public string SessionId { get; set; } = string.Empty;

        /// <summary>
        ///     Gets or sets the client identifier.
        /// </summary>
        public string ClientId { get; set; } = string.Empty;

        /// <summary>
        ///     Gets or sets the API key used for this session.
        /// </summary>
        public string ApiKey { get; set; } = string.Empty;

        /// <summary>
        ///     Gets or sets the time when the session was created.
        /// </summary>
        public DateTime ConnectedAt { get; set; }

        /// <summary>
        ///     Gets or sets the time of the last activity on this session.
        /// </summary>
        public DateTime LastActivity { get; set; }

        /// <summary>
        ///     Gets the connected time as UTC ticks for the gRPC response.
        /// </summary>
        public long ConnectedSinceUtcTicks => ConnectedAt.Ticks;
    }
}
