using System;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Xunit;
using ZB.MOM.WW.LmxProxy.Host.Sessions;

namespace ZB.MOM.WW.LmxProxy.Host.Tests.Sessions
{
    public class SessionManagerTests
    {
        [Fact]
        public void CreateSession_Returns32CharHexId()
        {
            using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
            var id = sm.CreateSession("client1", "key1");
            id.Should().HaveLength(32);
            id.Should().MatchRegex("^[0-9a-f]{32}$");
        }

        [Fact]
        public void CreateSession_IncrementsCount()
        {
            using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
            sm.ActiveSessionCount.Should().Be(0);
            sm.CreateSession("c1", "k1");
            sm.ActiveSessionCount.Should().Be(1);
            sm.CreateSession("c2", "k2");
            sm.ActiveSessionCount.Should().Be(2);
        }

        [Fact]
        public void ValidateSession_ReturnsTrueForExistingSession()
        {
            using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
            var id = sm.CreateSession("c1", "k1");
            sm.ValidateSession(id).Should().BeTrue();
        }

        [Fact]
        public void ValidateSession_ReturnsFalseForUnknownSession()
        {
            using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
            sm.ValidateSession("nonexistent").Should().BeFalse();
        }

        [Fact]
        public void ValidateSession_UpdatesLastActivity()
        {
            using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
            var id = sm.CreateSession("c1", "k1");
            var session = sm.GetSession(id);
            var initialActivity = session!.LastActivity;

            Thread.Sleep(50); // Small delay to ensure time passes
            sm.ValidateSession(id);

            session.LastActivity.Should().BeAfter(initialActivity);
        }

        [Fact]
        public void TerminateSession_RemovesSession()
        {
            using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
            var id = sm.CreateSession("c1", "k1");
            sm.TerminateSession(id).Should().BeTrue();
            sm.ActiveSessionCount.Should().Be(0);
            sm.ValidateSession(id).Should().BeFalse();
        }

        [Fact]
        public void TerminateSession_ReturnsFalseForUnknownSession()
        {
            using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
            sm.TerminateSession("nonexistent").Should().BeFalse();
        }

        [Fact]
        public void GetSession_ReturnsNullForUnknown()
        {
            using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
            sm.GetSession("nonexistent").Should().BeNull();
        }

        [Fact]
        public void GetSession_ReturnsCorrectInfo()
        {
            using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
            var id = sm.CreateSession("client-abc", "key-xyz");
            var session = sm.GetSession(id);
            session.Should().NotBeNull();
            session!.ClientId.Should().Be("client-abc");
            session.ApiKey.Should().Be("key-xyz");
            session.SessionId.Should().Be(id);
            session.ConnectedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(2));
        }

        [Fact]
        public void GetAllSessions_ReturnsSnapshot()
        {
            using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
            sm.CreateSession("c1", "k1");
            sm.CreateSession("c2", "k2");
            var all = sm.GetAllSessions();
            all.Should().HaveCount(2);
        }

        [Fact]
        public async Task ConcurrentAccess_IsThreadSafe()
        {
            using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
            var tasks = new Task[100];
            for (int i = 0; i < 100; i++)
            {
                int idx = i;
                tasks[i] = Task.Run(() =>
                {
                    var id = sm.CreateSession($"client-{idx}", $"key-{idx}");
                    sm.ValidateSession(id);
                    if (idx % 3 == 0) sm.TerminateSession(id);
                });
            }
            await Task.WhenAll(tasks);

            // Should have ~67 sessions remaining (100 - ~33 terminated)
            sm.ActiveSessionCount.Should().BeInRange(60, 70);
        }

        [Fact]
        public void Dispose_ClearsAllSessions()
        {
            var sm = new SessionManager(inactivityTimeoutMinutes: 0);
            sm.CreateSession("c1", "k1");
            sm.CreateSession("c2", "k2");
            sm.Dispose();
            sm.ActiveSessionCount.Should().Be(0);
        }

        [Fact]
        public void ConnectedSinceUtcTicks_ReturnsCorrectValue()
        {
            using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
            var id = sm.CreateSession("c1", "k1");
            var session = sm.GetSession(id);
            session!.ConnectedSinceUtcTicks.Should().Be(session.ConnectedAt.Ticks);
        }
    }
}
