using AsbCloudApp.Data;
using AsbCloudApp.Data.WITS;
using AsbCloudApp.Repositories;
using AsbCloudInfrastructure.Services.Trajectory;
using Moq;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace AsbCloudWebApi.Tests.ServicesTests
{
    public class TrajectoryVisualizationServiceTest
    {
        private Mock<T> MakeTrajectoryRepositoryMock<T>(IEnumerable<TrajectoryGeoFactDto> dateForGetMethod)
            where T : class, ITrajectoryPlanRepository
        {
            var mock = new Mock<T>();

            mock.Setup(r => r.GetAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
                .Returns(Task.FromResult(dateForGetMethod));

            return mock;
        }

        [Fact]
        public async Task GetTrajectoryAsync_SameCounts()
        {
            var plannedTrajectory = new TrajectoryGeoFactDto[]
            {
                new(0d, 0d, 0d),
                new(0d, 0d, 10d),
                new(0d, 30d, 20d),
                new(30d, 0d, 30d),
                new(30d, 90d, 40d),
                new(0d, 0d, 50d),
            };

            var actualTrajectory = new TrajectoryGeoFactDto[]
            {
                new(0, 0, 0),
                new(30,30,10),
                new(0, 0, 20),
            };

            var mockPlan = MakeTrajectoryRepositoryMock<ITrajectoryPlanRepository>(plannedTrajectory);
            var mockFact = MakeTrajectoryRepositoryMock<ITrajectoryFactRepository>(actualTrajectory);
            var service = new TrajectoryService(mockPlan.Object, mockFact.Object);
            var result = await service.GetTrajectoryCartesianAsync(1, CancellationToken.None);
            Assert.Equal(plannedTrajectory.Length, result.Plan?.Count());
            Assert.Equal(actualTrajectory.Length, result.Fact?.Count());
        }

        [Fact]
        public async Task GetTrajectoryAsync_StraigthBore()
        {
            var trajectory = new TrajectoryGeoFactDto[]
            {
                new(0, 0, 0),
                new(0, 0, 0),
                new(20, 0, 0),
                new(20, 0, 0),
                new(30, 0, 0),
                new(50, 0, 0),
            };

            var mockPlan = MakeTrajectoryRepositoryMock<ITrajectoryPlanRepository>(trajectory);
            var mockFact = MakeTrajectoryRepositoryMock<ITrajectoryFactRepository>(trajectory);
            var service = new TrajectoryService(mockPlan.Object, mockFact.Object);
            var result = await service.GetTrajectoryCartesianAsync(1, CancellationToken.None);
            var lastPointPlan = result.Plan!.Last();
            var lastPointFact = result.Fact!.Last();

            Assert.Equal(0d, lastPointPlan.X, 0.1d);
            Assert.Equal(0d, lastPointPlan.Y, 0.1d);
            Assert.Equal(50d, lastPointPlan.Z, 0.1d);

            Assert.Equal(0d, lastPointFact.X, 0.1d);
            Assert.Equal(0d, lastPointFact.Y, 0.1d);
            Assert.Equal(50d, lastPointFact.Z, 0.1d);
        }

        [Fact]
        public async Task GetTrajectoryAsync_Match()
        {
            var trajectory = new TrajectoryGeoFactDto[]
            {
                new(0, 0, 0),
                new(10, 30, 30),
                new(20, 0, 0),
            };

            var mockPlanned = MakeTrajectoryRepositoryMock<ITrajectoryPlanRepository>(trajectory);
            var mockFactual = MakeTrajectoryRepositoryMock<ITrajectoryFactRepository>(trajectory);
            var service = new TrajectoryService(mockPlanned.Object, mockFactual.Object);
            var result = await service.GetTrajectoryCartesianAsync(1, CancellationToken.None);
            var lastPointPlan = result.Plan!.Last();
            var lastPointFact = result.Fact!.Last();
            var tolerancePlan = 0.001d;
            var toleranceFact = 0.001d;

            Assert.InRange(lastPointPlan.Z, 10 + tolerancePlan, 20 - tolerancePlan);
            Assert.InRange(lastPointPlan.Y, 0 + tolerancePlan, 10 - tolerancePlan);
            Assert.InRange(lastPointPlan.X, 0 + tolerancePlan, 10 - tolerancePlan);

            Assert.InRange(lastPointFact.Z, 10 + toleranceFact, 20 - toleranceFact);
            Assert.InRange(lastPointFact.Y, 0 + toleranceFact, 10 - toleranceFact);
            Assert.InRange(lastPointFact.X, 0 + toleranceFact, 10 - toleranceFact);
        }
    }
}