using Xunit;
using CacheModule;

namespace CacheModuleTest{
    public class CacheServiceUnitTest
    {
        [Fact]
        public void Instance_WhenCalled_ReturnsSameInstance()
        {
            // Arrange
            var cacheService1 = CacheService.Instance;
            var cacheService2 = CacheService.Instance;

            // Act

            // Assert
            Assert.Same(cacheService1, cacheService2);
        }

        [Fact]
        public void Add_WhenCalledWithinDuration_AddsToCache()
        {
            // Arrange
            var cacheService = CacheService.Instance;
            cacheService.Clear();
            var functionName = "functionName";
            var functionParams = "functionParams";
            var value = new object();

            // Act
            cacheService.Add(functionName, functionParams, value, Duration.OneMinute);

            // Assert
            Assert.Equal(value, cacheService.Get(functionName, functionParams));
        }

        [Fact]
        public void Add_WhenCalledAfterDuration_RemovesFromCache()
        {
            // Arrange
            var cacheService = CacheService.Instance;
            cacheService.Clear();
            var functionName = "functionName";
            var functionParams = "functionParams";
            var value = new object();

            // Act
            cacheService.Add(functionName, functionParams, value, Duration.OneSecond);
            Thread.Sleep(2000);

            // Assert
            Assert.Null(cacheService.Get(functionName, functionParams));
        }

        [Fact]
        public void Add_WhenCalledWithSameFunctionNameAndFunctionParams_OverwritesCache()
        {
            // Arrange
            var cacheService = CacheService.Instance;
            cacheService.Clear();
            var functionName = "functionName";
            var functionParams = "functionParams";
            var value1 = new object();
            var value2 = new object();

            // Act
            cacheService.Add(functionName, functionParams, value1, Duration.OneMinute);
            cacheService.Add(functionName, functionParams, value2, Duration.OneMinute);

            // Assert
            Assert.Equal(value2, cacheService.Get(functionName, functionParams));
        }

        [Fact]
        public void Get_WhenCalledWithNonExistingFunctionName_ReturnsNull()
        {
            // Arrange
            var cacheService = CacheService.Instance;
            cacheService.Clear();
            var functionName = "functionName";
            var functionParams = "functionParams";

            // Act

            // Assert
            Assert.Null(cacheService.Get(functionName, functionParams));
        }

        [Fact]
        public void Get_WhenCalledWithNonExistingFunctionParams_ReturnsNull()
        {
            // Arrange
            var cacheService = CacheService.Instance;
            cacheService.Clear();
            var functionName = "functionName";
            var functionParams = "functionParams";
            var value = new object();

            // Act
            cacheService.Add(functionName, functionParams, value, Duration.OneMinute);

            // Assert
            Assert.Null(cacheService.Get(functionName, "nonExistingFunctionParams"));
        }

        [Fact]
        public void Clear_WhenCalledWithParams_ClearsCache()
        {
            // Arrange
            var cacheService = CacheService.Instance;
            var functionName = "functionName";
            var functionParams = "functionParams";
            var value = new object();
            cacheService.Add(functionName, functionParams, value, Duration.OneMinute);

            // Act
            cacheService.Clear();

            // Assert
            Assert.Null(cacheService.Get(functionName, functionParams));
        }

        [Fact]
        public void Clear_WhenCalledWithFunctionName_ClearsCache()
        {
            // Arrange
            var cacheService = CacheService.Instance;
            var functionName = "functionName";
            var functionParams = "functionParams";
            var functionName2 = "functionName2";
            var functionParams2 = "functionParams2";
            var value = new object();
            var value2 = new object();
            cacheService.Add(functionName, functionParams, value, Duration.OneMinute);
            cacheService.Add(functionName2, functionParams2, value2, Duration.OneMinute);

            // Act
            cacheService.Clear(functionName);        

            // Assert
            Assert.Null(cacheService.Get(functionName, functionParams));
            Assert.Equal(value2, cacheService.Get(functionName2, functionParams2));
        }

        [Fact]
        public void Clear_WhenCalledWithFunctionNameAndFunctionParams_ClearsCache()
        {
            // Arrange
            var cacheService = CacheService.Instance;
            var functionName = "functionName";
            var functionParams = "functionParams";
            var functionParams2 = "functionParams2";
            var value = new object();
            var value2 = new object();
            cacheService.Add(functionName, functionParams, value, Duration.OneMinute);
            cacheService.Add(functionName, functionParams2, value2, Duration.OneMinute);

            // Act
            cacheService.Clear(functionName, functionParams);

            // Assert
            Assert.Null(cacheService.Get(functionName, functionParams));
            Assert.Equal(value2, cacheService.Get(functionName, functionParams2));
        }

        [Fact]
        public void ClearExpired_WhenCalled_ClearsExpiredCache()
        {
            // Arrange
            var cacheService = CacheService.Instance;
            var functionName = "functionName";
            var functionParams = "functionParams";
            var functionName2 = "functionName2";
            var functionParams2 = "functionParams2";
            var value = new object();
            cacheService.Add(functionName, functionParams, value, Duration.OneSecond);
            cacheService.Add(functionName2, functionParams2, value, Duration.OneMinute);
            Thread.Sleep(2000);

            // Act
            cacheService.ClearExpired();

            // Assert
            Assert.Null(cacheService.Get(functionName, functionParams));
            Assert.Equal(value, cacheService.Get(functionName2, functionParams2));
        }

        
    }
}