using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AsbCloudInfrastructure.Services
{

    public class LimitingParameterService : ILimitingParameterService
    {
        private readonly ILimitingParameterRepository limitingParameterRepository;
        private readonly IWellService wellService;
        private readonly  Dictionary<int, string> feedRegulatorData = new ()
        {
            { LimitingParameterDto.NoLimit, "Нет ограничения" },
            { LimitingParameterDto.RopPlan, "МСП" },
            { LimitingParameterDto.Pressure, "Давление" },
            { LimitingParameterDto.AxialLoad, "Осевая нагрузка" },
            { LimitingParameterDto.RotorTorque, "Момент" }
        };

        public LimitingParameterService(ILimitingParameterRepository limitingParameterRepository,
            IWellService wellService)
        {
            this.limitingParameterRepository = limitingParameterRepository;
            this.wellService = wellService;
        }

        public async Task<IEnumerable<LimitingParameterDto>> GetStatAsync(LimitingParameterRequest request, CancellationToken token)
        {
            var well = await wellService.GetOrDefaultAsync(request.IdWell, token);
            if (well?.IdTelemetry is null || well.Timezone is null)
                return Enumerable.Empty<LimitingParameterDto>();

            var data = (await limitingParameterRepository.GetLimitingParametersAsync(request, well, token))
                .GroupBy(x => x.IdFeedRegulator);

            List<LimitingParameterDto> result = new List<LimitingParameterDto>(data.Count());
            foreach (var item in data)
            {
                var trimData = TrimLimitingParameters(item, request).ToArray();
                
                var allItemDepths = trimData.Sum(x => x.DepthEnd - x.DepthStart);
                var allItemDates = trimData.Sum(x => (x.DateEnd - x.DateStart).TotalMinutes);

                result.Add(new LimitingParameterDto
                {
                    IdWell = well.Id,
                    IdFeedRegulator = item.Key,
                    Depth = allItemDepths,
                    TotalMinutes = (float)allItemDates,
                    NumberInclusions = item.Count(),
                    NameFeedRegulator = feedRegulatorData.GetValueOrDefault(item.Key) ?? $"Id: {item.Key}"
                });
            }

            return result;
        }

        public Dictionary<int, string> GetLimitingParameteraNames() //TODO: Перенести получение ограничений в репозиторий
        {
            return feedRegulatorData;
        }

        private IEnumerable<LimitingParameterDataDto> TrimLimitingParameters(IEnumerable<LimitingParameterDataDto> data, LimitingParameterRequest request)
        {
            var result = data.Select((x) =>
            {
                if (request.GtDate.HasValue && x.DateStart < request.GtDate.Value)
                {
                    x.DepthStart = GetDepth(request.GtDate.Value, x);
                    x.DateStart = request.GtDate.Value;
                }
                if (request.LtDate.HasValue && x.DateEnd > request.LtDate.Value)
                {
                    x.DepthEnd = GetDepth(request.LtDate.Value, x);
                    x.DateEnd = request.LtDate.Value;
                }

                if (request.GtDepth.HasValue && x.DepthStart < request.GtDepth.Value)
                {
                    x.DateStart = GetDate(request.GtDepth.Value, x);
                    x.DepthStart = (float)request.GtDepth.Value;
                }
                if (request.LtDepth.HasValue && x.DepthEnd > request.LtDepth.Value)
                {
                    x.DateEnd = GetDate(request.LtDepth.Value, x);
                    x.DepthEnd = (float)request.LtDepth.Value;
                }
                return x;
            });
            return result;
        }

        private float GetDepth(DateTimeOffset date, LimitingParameterDataDto dto)
        {
            var a = (date - dto.DateStart).TotalSeconds;
            var b = (dto.DateEnd - dto.DateStart).TotalSeconds;
            var c = dto.DepthEnd - dto.DepthStart;
            var result = dto.DepthStart + (a / b) * c;
            return (float)result;
        }

        private DateTime GetDate(double depth, LimitingParameterDataDto dto)
        {
            var a = depth - dto.DepthStart;
            var b = dto.DepthEnd - dto.DepthStart;
            var c = (dto.DateEnd - dto.DateStart);
            var result = dto.DateStart + (a / b) * c;
            return result;
        }
    }

}