using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Persistence.Client;
using Persistence.Client.Clients.Interfaces;
using Persistence.Database.Entity;
using Persistence.Models;
using Persistence.Models.Requests;
using System.Net;
using Xunit;

namespace Persistence.IntegrationTests.Controllers
{
    public class TechMessagesControllerTest : BaseIntegrationTest
    {
        private static readonly string SystemCacheKey = $"{typeof(Database.Entity.DrillingSystem).FullName}CacheKey";
        private readonly ITechMessagesClient techMessagesClient;
        private readonly IMemoryCache memoryCache;
        public TechMessagesControllerTest(WebAppFactoryFixture factory) : base(factory)
        {
            var scope = factory.Services.CreateScope();
            var persistenceClientFactory = scope.ServiceProvider
                .GetRequiredService<PersistenceClientFactory>();

			techMessagesClient = persistenceClientFactory.GetTechMessagesClient();
			memoryCache = scope.ServiceProvider.GetRequiredService<IMemoryCache>();
		}

        [Fact]
        public async Task GetPage_returns_success()
        {
            //arrange
            memoryCache.Remove(SystemCacheKey);
            dbContext.CleanupDbSet<TechMessage>();
            dbContext.CleanupDbSet<Database.Entity.DrillingSystem>();

            var requestDto = new PaginationRequest()
            {
                Skip = 1,
                Take = 2,
                SortSettings = nameof(TechMessage.CategoryId)
            };

            //act
            var response = await techMessagesClient.GetPage(requestDto, CancellationToken.None);

			//assert
			Assert.NotNull(response);
			Assert.Empty(response.Items);
			Assert.Equal(requestDto.Skip, response.Skip);
			Assert.Equal(requestDto.Take, response.Take);
		}

        [Fact]
        public async Task GetPage_AfterSave_returns_success()
        {
            //arrange
            var dtos = await InsertRange();
            var dtosCount = dtos.Count();
            var requestDto = new PaginationRequest()
            {
                Skip = 0,
                Take = 2,
                SortSettings = nameof(TechMessage.CategoryId)
            };

            //act
            var response = await techMessagesClient.GetPage(requestDto, CancellationToken.None);

			//assert
			Assert.NotNull(response);
			Assert.Equal(dtosCount, response.Count);
		}

        [Fact]
        public async Task InsertRange_returns_success()
        {
            await InsertRange();
        }

		[Fact]
		public async Task InsertRange_returns_BadRequest()
		{
			//arrange
			const string exceptionMessage = "Ошибка валидации, формата или маршрутизации запроса";
			var dtos = new List<TechMessageDto>()
			{
				new TechMessageDto()
				{
					EventId = Guid.NewGuid(),
					CategoryId = -1, // < 0
					Timestamp = DateTimeOffset.UtcNow,
                    Depth = -1, // < 0
					MessageText = string.Empty, // length < 0
					System = string.Concat(Enumerable.Repeat(nameof(TechMessageDto.System), 100)), // length > 256
					UserId = Guid.NewGuid()
                }
            };

			try
			{
				//act
				var response = await techMessagesClient.AddRange(dtos, new CancellationToken());
			}
			catch (Exception ex)
			{
				//assert
				Assert.Equal(exceptionMessage, ex.Message);
			}
		}

        [Fact]
        public async Task GetSystems_returns_success()
        {
            //arrange
            memoryCache.Remove(SystemCacheKey);
            dbContext.CleanupDbSet<TechMessage>();
            dbContext.CleanupDbSet<Database.Entity.DrillingSystem>();

            //act
            var response = await techMessagesClient.GetSystems(CancellationToken.None);

			//assert
			Assert.NotNull(response);
			Assert.Empty(response);
		}

        [Fact]
        public async Task GetSystems_AfterSave_returns_success()
        {
            //arrange
            var dtos = await InsertRange();
            var systems = dtos
                .Select(e => e.System)
                .Distinct()
                .ToArray();

            //act
            var response = await techMessagesClient.GetSystems(CancellationToken.None);

			//assert
			Assert.NotNull(response);
			string?[]? content = response?.ToArray();
			Assert.Equal(systems, content);
		}

        [Fact]
        public async Task GetStatistics_returns_success()
        {
            //arrange
            memoryCache.Remove(SystemCacheKey);
            dbContext.CleanupDbSet<TechMessage>();
            dbContext.CleanupDbSet<Database.Entity.DrillingSystem>();

            var importantId = 1;
            var autoDrillingSystem = nameof(TechMessageDto.System);

            //act
            var response = await techMessagesClient.GetStatistics(autoDrillingSystem, importantId, CancellationToken.None);

			//assert
			Assert.NotNull(response);
			Assert.Empty(response);
		}

        [Fact]
        public async Task GetStatistics_AfterSave_returns_success()
        {
            //arrange
            var importantId = 0;
            var autoDrillingSystem = nameof(TechMessageDto.System);
            var dtos = await InsertRange();
            var filteredDtos = dtos.Where(e => e.CategoryId == importantId && e.System == autoDrillingSystem);

            //act
            var response = await techMessagesClient.GetStatistics(autoDrillingSystem, importantId, CancellationToken.None);

			//assert
			Assert.NotNull(response);
			var categories = response
				.FirstOrDefault()?.Categories
				.FirstOrDefault(e => e.Key == 0).Value;
			Assert.Equal(filteredDtos.Count(), categories);
		}

		[Fact]
		public async Task GetDatesRange_returns_success()
		{
			//arrange
			memoryCache.Remove(SystemCacheKey);
			dbContext.CleanupDbSet<TechMessage>();
			dbContext.CleanupDbSet<DrillingSystem>();

			//act
			var response = await techMessagesClient.GetDatesRangeAsync(CancellationToken.None);

			//assert
			Assert.NotNull(response);
			Assert.Equal(DateTimeOffset.MinValue, response?.From);
			Assert.Equal(DateTimeOffset.MaxValue, response?.To);
		}

        [Fact]
        public async Task GetDatesRange_AfterSave_returns_success()
        {
            //arrange
            await InsertRange();

            //act
            var response = await techMessagesClient.GetDatesRangeAsync(CancellationToken.None);

			//assert
			Assert.NotNull(response);
			Assert.NotNull(response?.From);
			Assert.NotNull(response?.To);
		}

        [Fact]
        public async Task GetPart_returns_success()
        {
            //arrange
            var dateBegin = DateTimeOffset.UtcNow;
            var take = 2;

            //act
            var response = await techMessagesClient.GetPart(dateBegin, take, CancellationToken.None);

			//assert
			Assert.NotNull(response);
			Assert.Empty(response);
		}

        [Fact]
        public async Task GetPart_AfterSave_returns_success()
        {
            //arrange
            var dateBegin = DateTimeOffset.UtcNow;
            var take = 1;
            await InsertRange();

            //act
            var response = await techMessagesClient.GetPart(dateBegin, take, CancellationToken.None);

			//assert
			Assert.NotNull(response);
			Assert.NotEmpty(response);
		}

        private async Task<IEnumerable<TechMessageDto>> InsertRange()
        {
            //arrange
            memoryCache.Remove(SystemCacheKey);
            dbContext.CleanupDbSet<TechMessage>();
            dbContext.CleanupDbSet<DrillingSystem>();

            var dtos = new List<TechMessageDto>()
            {
                new()
                {
                    EventId = Guid.NewGuid(),
                    CategoryId = 1,
                    Timestamp = DateTimeOffset.UtcNow,
                    Depth = 1.11,
                    MessageText = nameof(TechMessageDto.MessageText),
                    System = nameof(TechMessageDto.System).ToLower(),
                    UserId = Guid.NewGuid()
                },
                new()
                {
                    EventId = Guid.NewGuid(),
                    CategoryId = 2,
                    Timestamp = DateTimeOffset.UtcNow,
                    Depth = 2.22,
                    MessageText = nameof(TechMessageDto.MessageText),
                    System = nameof(TechMessageDto.System).ToLower(),
                    UserId = Guid.NewGuid()
                }
            };


            //act
            var response = await techMessagesClient.AddRange(dtos, CancellationToken.None);

			//assert
			Assert.Equal(dtos.Count, response);

            return dtos;
        }
    }
}