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 Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Xunit;

namespace DD.Persistence.IntegrationTests.Controllers;
public class WitsDataControllerTest : BaseIntegrationTest
{
    private IWitsDataClient witsDataClient;

    public WitsDataControllerTest(WebAppFactoryFixture factory) : base(factory)
    {
        var refitClientFactory = scope.ServiceProvider
           .GetRequiredService<IRefitClientFactory<IRefitWitsDataClient>>();
        var logger = scope.ServiceProvider.GetRequiredService<ILogger<WitsDataClient>>();

        witsDataClient = scope.ServiceProvider
            .GetRequiredService<IWitsDataClient>();
    }

    [Fact]
    public async Task GetDatesRangeAsync_returns_success()
    {
        //arrange
        dbContext.CleanupDbSet<ParameterData>();

        var discriminatorId = Guid.NewGuid();

        //act
        var response = await witsDataClient.GetDatesRangeAsync(discriminatorId, CancellationToken.None);

        //assert
        Assert.NotNull(response);
    }

    [Fact]
    public async Task GetPart_returns_success()
    {
        //arrange
        dbContext.CleanupDbSet<ParameterData>();

        var discriminatorId = Guid.NewGuid();
        var dateBegin = DateTimeOffset.UtcNow;
        var take = 1;

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

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

    [Fact]
    public async Task InsertRange_returns_success()
    {
        //arrange
        dbContext.CleanupDbSet<ParameterData>();

        //act
        await AddRange();
    }

    [Fact]
    public async Task GetValuesForGraph_returns_success()
    {
        //arrange
        dbContext.CleanupDbSet<ParameterData>();

        var discriminatorId = Guid.NewGuid();
        var dateFrom = DateTimeOffset.UtcNow;
        var dateTo = DateTimeOffset.UtcNow;
        var approxPointCount = 12;

        //act
        var response = await witsDataClient.GetValuesForGraph(discriminatorId, dateFrom, dateTo, approxPointCount, CancellationToken.None);

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

    [Fact]
    public async Task GetDatesRangeAsync_AfterSave_returns_success()
    {
        //arrange
        dbContext.CleanupDbSet<ParameterData>();

        var dtos = await AddRange();
        var discriminatorId = dtos.FirstOrDefault()!.DiscriminatorId;

        //act
        var response = await witsDataClient.GetDatesRangeAsync(discriminatorId, CancellationToken.None);

        //assert
        Assert.NotNull(response);

        var expectedDateFrom = dtos
            .Select(e => e.Timestamped)
            .Min()
            .ToString("dd.MM.yyyy-HH:mm:ss");
        var actualDateFrom = response.From.DateTime
            .ToString("dd.MM.yyyy-HH:mm:ss");
        Assert.Equal(expectedDateFrom, actualDateFrom);

        var expectedDateTo = dtos
            .Select(e => e.Timestamped)
            .Max()
            .ToString("dd.MM.yyyy-HH:mm:ss");
        var actualDateTo = response.To.DateTime
            .ToString("dd.MM.yyyy-HH:mm:ss");
        Assert.Equal(expectedDateTo, actualDateTo);
    }

    [Fact]
    public async Task GetPart_AfterSave_returns_success()
    {
        //arrange
        dbContext.CleanupDbSet<ParameterData>();

        var dtos = await AddRange();
        var discriminatorId = dtos.FirstOrDefault()!.DiscriminatorId;
        var dateBegin = dtos.FirstOrDefault()!.Timestamped;
        var take = 1;

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

        //assert
        Assert.NotNull(response);
        Assert.NotEmpty(response);
        Assert.Equal(take, response.Count());

        var expectedDto = dtos.FirstOrDefault();
        var actualDto = response.FirstOrDefault();
        Assert.Equal(expectedDto?.DiscriminatorId, actualDto?.DiscriminatorId);

        var expectedValueDto = expectedDto?.Values.FirstOrDefault();
        var actualValueDto = actualDto?.Values.FirstOrDefault();
        Assert.Equal(expectedValueDto?.ItemId, actualValueDto?.ItemId);
        Assert.Equal(expectedValueDto?.RecordId, actualValueDto?.RecordId);
    }

    [Fact]
    public async Task GetValuesForGraph_AfterSave_returns_success()
    {
        //arrange
        dbContext.CleanupDbSet<ParameterData>();

        var dtos = await AddRange(37);
        var discriminatorId = dtos.FirstOrDefault()!.DiscriminatorId;
        var dateFrom = dtos.Select(e => e.Timestamped).Min();
        var dateTo = dtos.Select(e => e.Timestamped).Max();
        var approxPointCount = 12;

        //act
        var response = await witsDataClient.GetValuesForGraph(discriminatorId, dateFrom, dateTo, approxPointCount, CancellationToken.None);

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

    [Fact]
    public async Task AddRange_returns_BadRequest()
    {
        //arrange
        const string exceptionMessage = "Ошибка валидации, формата или маршрутизации запроса";
        var dtos = new List<WitsDataDto>()
        {
            new WitsDataDto()
            {
                DiscriminatorId = Guid.NewGuid(),
                Timestamped = DateTimeOffset.UtcNow,
                Values = new List<WitsValueDto>()
                {
                    new WitsValueDto()
                    {
                        RecordId = -1, // < 0
						ItemId = 101, // > 100
						Value = string.Empty
                    }
                }
            }
        };

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

    private async Task<IEnumerable<WitsDataDto>> AddRange(int countToCreate = 10)
    {
        var dtos = new List<WitsDataDto>();
        var discriminatorId = Guid.NewGuid();
        var timestamped = DateTimeOffset.UtcNow;
        for (var i = 0; i < countToCreate; i++)
        {
            var random = new Random();
            dtos.Add(new WitsDataDto()
            {
                DiscriminatorId = discriminatorId,
                Timestamped = timestamped.AddSeconds(i),
                Values = new List<WitsValueDto>()
                {
                    new WitsValueDto()
                    {
                        RecordId = i + 1,
                        ItemId = i + 1,
                        Value = random.Next(1, 100)
                    }
                }
            });
        }

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

        //assert
        var count = dtos.SelectMany(e => e.Values).Count();
        Assert.Equal(count, response);

        return dtos;
    }
}