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

namespace DD.Persistence.IntegrationTests.Controllers
{
	public class TechMessagesControllerTest : BaseIntegrationTest
	{
		private static readonly string SystemCacheKey = $"{typeof(Database.Entity.DataSourceSystem).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<DataSourceSystem>();

            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(Guid.NewGuid());
			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(Guid.NewGuid());
		}

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

			try
			{
				//act
				var response = await techMessagesClient.AddRange(systemId, dtos, CancellationToken.None);
			}
			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<DataSourceSystem>();

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

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

		[Fact]
		public async Task GetSystems_AfterSave_returns_success()
		{
			//arrange
			await InsertRange(Guid.NewGuid());

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

			//assert
			Assert.NotNull(response);
			var expectedSystemCount = 1;
			Assert.Equal(expectedSystemCount, response!.Count());
		}

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

			var categoryIds = new [] { 1, 2 };
			var systemIds = new [] { Guid.NewGuid() };

			//act
			var response = await techMessagesClient.GetStatistics(systemIds, categoryIds, CancellationToken.None);

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

		[Fact]
		public async Task GetStatistics_AfterSave_returns_success()
		{
			//arrange
			var categoryIds = new[] { 1 };
			var systemId = Guid.NewGuid();
			var dtos = await InsertRange(systemId);
			var filteredDtos = dtos.Where(e => categoryIds.Contains(e.CategoryId));

			//act
			var response = await techMessagesClient.GetStatistics([systemId], categoryIds, CancellationToken.None);

			//assert
			Assert.NotNull(response);
			Assert.NotEmpty(response);
			var categories = response
				.FirstOrDefault()!.Categories
				.Count();
			Assert.Equal(filteredDtos.Count(), categories);
		}

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

            //act
            var response = await techMessagesClient.GetDatesRangeAsync(CancellationToken.None);
            
            //assert
            Assert.Null(response);		
		}

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

            //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(Guid.NewGuid());

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

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

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

			var dtos = new List<TechMessageDto>()
			{
				new TechMessageDto()
				{
					EventId = Guid.NewGuid(),
					CategoryId = 1,
					Timestamp = DateTimeOffset.UtcNow,
					Text = nameof(TechMessageDto.Text),
					EventState = Models.Enumerations.EventState.Triggered
				},
				new TechMessageDto()
				{
					EventId = Guid.NewGuid(),
					CategoryId = 2,
					Timestamp = DateTimeOffset.UtcNow,
					Text = nameof(TechMessageDto.Text),
					EventState = Models.Enumerations.EventState.Triggered
				}
			};


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

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

            return dtos;
        }
    }
}