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

namespace DD.Persistence.IntegrationTests.Controllers
{
    public class SetpointControllerTest : BaseIntegrationTest
    {
        private readonly ISetpointClient setpointClient;
        private readonly SetpointConfigStorage configStorage;
        public SetpointControllerTest(WebAppFactoryFixture factory) : base(factory)
        {
            var refitClientFactory = scope.ServiceProvider
            .GetRequiredService<IRefitClientFactory<IRefitSetpointClient>>();
            var logger = scope.ServiceProvider.GetRequiredService<ILogger<SetpointClient>>();

            setpointClient = scope.ServiceProvider
                .GetRequiredService<ISetpointClient>();

            configStorage = (SetpointConfigStorage)scope.ServiceProvider.GetRequiredService<ISetpointConfigStorage>();
        }


        [Fact]
        public async Task GetCurrent_returns_correctType()
        {
            var id = Guid.Parse("e0fcad22-1761-476e-a729-a3c59d51ba41");

            configStorage.AddOrReplace(id, typeof(float));

            await setpointClient.Add(id, 48.3f, CancellationToken.None);

            //act
            var response = await setpointClient.GetCurrent([id], CancellationToken.None);

            //assert
            Assert.NotNull(response);
            Assert.NotEmpty(response);
            Assert.Single(response);
            var item = response.First();
            Assert.Equal(item.Key, id);

            Assert.IsNotType<JsonElement>(item.Value);
            Assert.Equal(item.Value, 48.3f);
        }



        [Fact]
        public async Task GetCurrent_returns_success()
        {
            //arrange
            var setpointKeys = new List<Guid>()
            {
                Guid.NewGuid(),
                Guid.NewGuid()
            };

            //act
            var response = await setpointClient.GetCurrent(setpointKeys, CancellationToken.None);

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

        [Fact]
        public async Task GetCurrent_AfterSave_returns_success()
        {
            //arrange
            var setpointKey = await Add();

            //act
            var response = await setpointClient.GetCurrent([setpointKey], new CancellationToken());

            //assert
            Assert.NotNull(response);
            Assert.NotEmpty(response);
            Assert.Equal(setpointKey, response.FirstOrDefault()?.Key);
        }

        [Fact]
        public async Task GetHistory_returns_success()
        {
            //arrange
            var setpointKeys = new List<Guid>()
            {
                Guid.NewGuid(),
                Guid.NewGuid()
            };
            var historyMoment = DateTimeOffset.UtcNow;

            //act
            var response = await setpointClient.GetHistory(setpointKeys, historyMoment, new CancellationToken());

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

        [Fact]
        public async Task GetHistory_AfterSave_returns_success()
        {
            //arrange
            var setpointKey = await Add();
            var historyMoment = DateTimeOffset.UtcNow;
            historyMoment = historyMoment.AddDays(1);

            //act
            var response = await setpointClient.GetHistory([setpointKey], historyMoment, new CancellationToken());

            //assert
            Assert.NotNull(response);
            Assert.NotEmpty(response);
            Assert.Equal(setpointKey, response.FirstOrDefault()?.Key);
        }

        [Fact]
        public async Task GetLog_returns_success()
        {
            //arrange
            var setpointKeys = new List<Guid>()
            {
                Guid.NewGuid(),
                Guid.NewGuid()
            };

            //act
            var response = await setpointClient.GetLog(setpointKeys, new CancellationToken());

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

        [Fact]
        public async Task GetLog_AfterSave_returns_success()
        {
            //arrange
            var setpointKey = await Add();

            //act
            var response = await setpointClient.GetLog([setpointKey], new CancellationToken());

            //assert
            Assert.NotNull(response);
            Assert.NotEmpty(response);
            Assert.Equal(setpointKey, response.FirstOrDefault().Key);
        }

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

            //act
            var response = await setpointClient.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
            dbContext.CleanupDbSet<Setpoint>();

            await Add();

            var dateBegin = DateTimeOffset.UtcNow.AddDays(-1);
            var take = 1;
            var part = await setpointClient.GetPart(dateBegin, take, CancellationToken.None);

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

            //assert
            Assert.NotNull(response);

            var expectedValue = part!
                .FirstOrDefault()!.Timestamp
                .ToUniversalTime()
                .ToString();
            var actualValueFrom = response.From
                .ToUniversalTime()
                .ToString();
            Assert.Equal(expectedValue, actualValueFrom);

            var actualValueTo = response.To
                .ToUniversalTime()
                .ToString();
            Assert.Equal(expectedValue, actualValueTo);
        }

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

            //act
            var response = await setpointClient.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 Add();

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

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

        [Fact]
        public async Task Save_returns_success()
        {
            await Add();
        }

        private async Task<Guid> Add()
        {
            //arrange
            var setpointKey = Guid.NewGuid();
            var setpointValue = new
            {
                Value1 = "1",
                Value2 = 2
            };

            //act
            await setpointClient.Add(setpointKey, setpointValue, new CancellationToken());

            return setpointKey;
        }
    }
}