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

namespace DD.Persistence.IntegrationTests.Controllers
{
    public class TechMessagesControllerTest : BaseIntegrationTest
    {
        private static readonly string SystemCacheKey = $"{typeof(DataSourceSystem).FullName}CacheKey";
        private readonly ITechMessagesClient techMessagesClient;
        private readonly IMemoryCache memoryCache;
        public TechMessagesControllerTest(WebAppFactoryFixture factory) : base(factory)
        {
            var refitClientFactory = scope.ServiceProvider
            .GetRequiredService<IRefitClientFactory<IRefitTechMessagesClient>>();
            var logger = scope.ServiceProvider.GetRequiredService<ILogger<TechMessagesClient>>();

            techMessagesClient = scope.ServiceProvider
                .GetRequiredService<ITechMessagesClient>();
            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;
        }
    }
}