using System;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Moq;
using TeleBot.Models;
using TeleBot.Services;
using Xunit;

namespace TeleBot.Tests.Services
{
    public class SessionManagerTests
    {
        private readonly SessionManager _sessionManager;
        private readonly Mock<IConfiguration> _mockConfiguration;
        private readonly Mock<ILogger<SessionManager>> _mockLogger;
        private readonly Mock<IPrivacyService> _mockPrivacyService;
        private readonly Mock<IDistributedCache> _mockCache;

        public SessionManagerTests()
        {
            _mockConfiguration = new Mock<IConfiguration>();
            _mockLogger = new Mock<ILogger<SessionManager>>();
            _mockPrivacyService = new Mock<IPrivacyService>();
            _mockCache = new Mock<IDistributedCache>();

            // Set up default configuration
            _mockConfiguration.Setup(x => x["Privacy:SessionTimeoutMinutes"])
                .Returns("30");
            _mockConfiguration.Setup(x => x["Privacy:EphemeralByDefault"])
                .Returns("true");
            _mockConfiguration.Setup(x => x["Redis:Enabled"])
                .Returns("false");

            // Set up privacy service
            _mockPrivacyService.Setup(x => x.HashIdentifier(It.IsAny<long>()))
                .Returns<long>(id => $"HASH_{id}");

            _sessionManager = new SessionManager(
                _mockConfiguration.Object,
                _mockLogger.Object,
                _mockPrivacyService.Object,
                null // No cache for unit tests
            );
        }

        [Fact]
        public async Task GetOrCreateSessionAsync_NewUser_ShouldCreateSession()
        {
            // Arrange
            long telegramUserId = 123456;

            // Act
            var session = await _sessionManager.GetOrCreateSessionAsync(telegramUserId);

            // Assert
            session.Should().NotBeNull();
            session.HashedUserId.Should().Be($"HASH_{telegramUserId}");
            session.IsEphemeral.Should().BeTrue();
            session.State.Should().Be(SessionState.MainMenu);
            session.Cart.Should().NotBeNull();
            session.Cart.IsEmpty().Should().BeTrue();
        }

        [Fact]
        public async Task GetOrCreateSessionAsync_ExistingUser_ShouldReturnSameSession()
        {
            // Arrange
            long telegramUserId = 123456;

            // Act
            var session1 = await _sessionManager.GetOrCreateSessionAsync(telegramUserId);
            var session2 = await _sessionManager.GetOrCreateSessionAsync(telegramUserId);

            // Assert
            session1.Id.Should().Be(session2.Id);
            session1.HashedUserId.Should().Be(session2.HashedUserId);
        }

        [Fact]
        public async Task UpdateSessionAsync_ShouldUpdateSession()
        {
            // Arrange
            long telegramUserId = 123456;
            var session = await _sessionManager.GetOrCreateSessionAsync(telegramUserId);
            
            // Act
            session.State = SessionState.BrowsingCategories;
            session.Cart.AddItem(Guid.NewGuid(), "Test Product", 10.00m, 2);
            await _sessionManager.UpdateSessionAsync(session);
            
            var retrievedSession = await _sessionManager.GetOrCreateSessionAsync(telegramUserId);

            // Assert
            retrievedSession.State.Should().Be(SessionState.BrowsingCategories);
            retrievedSession.Cart.GetTotalItems().Should().Be(2);
        }

        [Fact]
        public async Task DeleteSessionAsync_ShouldRemoveSession()
        {
            // Arrange
            long telegramUserId = 123456;
            var session = await _sessionManager.GetOrCreateSessionAsync(telegramUserId);
            var sessionId = session.Id;

            // Act
            await _sessionManager.DeleteSessionAsync(sessionId);
            var newSession = await _sessionManager.GetOrCreateSessionAsync(telegramUserId);

            // Assert
            newSession.Id.Should().NotBe(sessionId, "Should create a new session after deletion");
        }

        [Fact]
        public async Task DeleteUserDataAsync_ShouldRemoveAllUserSessions()
        {
            // Arrange
            long telegramUserId = 123456;
            var session = await _sessionManager.GetOrCreateSessionAsync(telegramUserId);
            var originalSessionId = session.Id;

            // Act
            await _sessionManager.DeleteUserDataAsync(telegramUserId);
            var newSession = await _sessionManager.GetOrCreateSessionAsync(telegramUserId);

            // Assert
            newSession.Id.Should().NotBe(originalSessionId);
            newSession.Cart.IsEmpty().Should().BeTrue();
            newSession.State.Should().Be(SessionState.MainMenu);
        }

        [Fact]
        public async Task CleanupExpiredSessionsAsync_ShouldRemoveExpiredSessions()
        {
            // Arrange
            long userId1 = 111111;
            long userId2 = 222222;
            
            var session1 = await _sessionManager.GetOrCreateSessionAsync(userId1);
            session1.ExpiresAt = DateTime.UtcNow.AddMinutes(-1); // Expired
            await _sessionManager.UpdateSessionAsync(session1);
            
            var session2 = await _sessionManager.GetOrCreateSessionAsync(userId2);
            // session2 not expired

            // Act
            await _sessionManager.CleanupExpiredSessionsAsync();
            
            var retrievedSession1 = await _sessionManager.GetOrCreateSessionAsync(userId1);
            var retrievedSession2 = await _sessionManager.GetOrCreateSessionAsync(userId2);

            // Assert
            retrievedSession1.Id.Should().NotBe(session1.Id, "Expired session should be recreated");
            retrievedSession2.Id.Should().Be(session2.Id, "Non-expired session should remain");
        }

        [Fact]
        public void Session_IsExpired_ShouldReturnCorrectStatus()
        {
            // Arrange
            var expiredSession = new UserSession
            {
                ExpiresAt = DateTime.UtcNow.AddMinutes(-1)
            };
            
            var activeSession = new UserSession
            {
                ExpiresAt = DateTime.UtcNow.AddMinutes(30)
            };

            // Assert
            expiredSession.IsExpired().Should().BeTrue();
            activeSession.IsExpired().Should().BeFalse();
        }

        [Fact]
        public void Session_UpdateActivity_ShouldUpdateLastActivityTime()
        {
            // Arrange
            var session = new UserSession();
            var originalTime = session.LastActivityAt;
            
            // Act
            System.Threading.Thread.Sleep(10); // Small delay
            session.UpdateActivity();

            // Assert
            session.LastActivityAt.Should().BeAfter(originalTime);
        }

        [Fact]
        public async Task GetOrCreateSessionAsync_WithPrivacySettings_ShouldApplyDefaults()
        {
            // Arrange
            _mockConfiguration.Setup(x => x["Privacy:EphemeralByDefault"])
                .Returns("true");
            _mockConfiguration.Setup(x => x["Features:EnableDisappearingMessages"])
                .Returns("true");
            
            long telegramUserId = 999999;

            // Act
            var session = await _sessionManager.GetOrCreateSessionAsync(telegramUserId);

            // Assert
            session.Privacy.Should().NotBeNull();
            session.Privacy.UseEphemeralMode.Should().BeTrue();
            session.Privacy.DisableAnalytics.Should().BeTrue();
            session.Privacy.EnableDisappearingMessages.Should().BeTrue();
        }
    }
}