using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Repository;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AsbCloudInfrastructure.Services.SAUB
{

    public class SetpointsService : ISetpointsService
    {
        // ## Инфо от АПГ от 26.11.2021, дополнения ШОВ от 26.11.2021
        private static readonly Dictionary<string, SetpointInfoDto> SetpointInfos = new()
        {
            { "bitLoad", new SetpointInfoDto { Name = "bitLoad", DisplayName = "Осевая нагрузка, т" } },
            { "dPressureMaxRotorSP", new SetpointInfoDto { Name = "dPressureMaxRotorSP", DisplayName = "Дифференциальное рабочее давление в роторе, атм" } },
            { "dPressureMaxSlideSP", new SetpointInfoDto { Name = "dPressureMaxSlideSP", DisplayName = "Дифференциальное рабочее давление в слайде, атм" } },
            { "torque", new SetpointInfoDto { Name = "torque", DisplayName = "Крутящий момент, кН*м" } },
            { "speedRotorSp", new SetpointInfoDto { Name = "speedRotorSp", DisplayName = "Скорость бурения в роторе, м/ч" } },
            { "speedSlideSp", new SetpointInfoDto { Name = "speedSlideSp", DisplayName = "Скорость бурения в слайде, м/ч" } },
            { "speedDevelopSp", new SetpointInfoDto { Name = "speedDevelopSp", DisplayName = "Скорость проработки, м/ч" } },
            { "torque_pid_out_limit", new SetpointInfoDto { Name = "torque_pid_out_limit", DisplayName = "Торк мастер. Допустимый процент отклонения от заданной скорости вращения" } }, // Такая же что и прямой
            //{ "", new SetpointInfoDto { Name = "", DisplayName = "Обороты ВСП, об/мин" } }, // Оно в ПЛК спинмастера, пока сделать нельзя, позднее можно.
            //{ "", new SetpointInfoDto { Name = "", DisplayName = "Расход промывочной жидкости, л/с" } }, // Нет в контроллере
        };
        private readonly SetpointsRequestRepository setpointsRepository;
        private readonly ITelemetryService telemetryService;

        public SetpointsService(IAsbCloudDbContext db, IMemoryCache memoryCache, ITelemetryService telemetryService, IWellService wellService)
        {
            setpointsRepository = new SetpointsRequestRepository(db, memoryCache, wellService);
            this.telemetryService = telemetryService;
        }

        public async Task<int> InsertAsync(SetpointsRequestDto setpointsRequest, CancellationToken token)
        {
            setpointsRequest.IdState = 1;
            setpointsRequest.UploadDate = DateTimeOffset.UtcNow;
            var result = await setpointsRepository.InsertAsync(setpointsRequest, token);
            return result;
        }

        public async Task<IEnumerable<SetpointsRequestDto>> GetAsync(int idWell, CancellationToken token)
        {
            var all = await setpointsRepository.GetAllAsync(token);
            var dtos = all.Where(s => s.IdWell == idWell);
            return dtos;
        }

        public async Task<IEnumerable<SetpointsRequestDto>> GetForPanelAsync(string uid, CancellationToken token)
        {
            var idWell = telemetryService.GetIdWellByTelemetryUid(uid) ?? -1;

            if (idWell < 0)
                return Enumerable.Empty<SetpointsRequestDto>();
            var all = await setpointsRepository.GetAllAsync(token);
            var filtered = all.Where(s =>
                s.IdWell == idWell &&
                s.IdState == 1 && 
                s.UploadDate.AddSeconds(s.ObsolescenceSec).UtcDateTime > DateTime.UtcNow)
                .ToArray();

            if (!filtered.Any())
                return Enumerable.Empty<SetpointsRequestDto>();

            foreach (var item in filtered)
                item.IdState = 2;

            await setpointsRepository.UpdateRangeAsync(filtered, token);

            return filtered;
        }

        public async Task<int> UpdateStateAsync(SetpointsRequestDto setpointsRequestDto, CancellationToken token)
        {
            if (setpointsRequestDto.IdState != 3 && setpointsRequestDto.IdState != 4)
                throw new ArgumentOutOfRangeException(nameof(setpointsRequestDto), $"{nameof(setpointsRequestDto.IdState)} = {setpointsRequestDto.IdState}. Mast be 3 or 4.");

            if (setpointsRequestDto.Id <= 0)
                throw new ArgumentOutOfRangeException(nameof(setpointsRequestDto), $"{nameof(setpointsRequestDto.Id)} = {setpointsRequestDto.Id}. Mast be > 0");

            if (setpointsRequestDto.IdWell <= 0)
                throw new ArgumentOutOfRangeException(nameof(setpointsRequestDto), $"{nameof(setpointsRequestDto.IdWell)} = {setpointsRequestDto.IdWell}. Mast be > 0");

            var entity = await setpointsRepository.GetOrDefaultAsync(setpointsRequestDto.Id, token);

            if (entity?.IdWell != setpointsRequestDto.IdWell)
                return 0;

            if (entity is null)
                return 0;

            entity.IdState = setpointsRequestDto.IdState;
            var affected = await setpointsRepository.UpdateRangeAsync(new[] { entity }, token);
            return affected;
        }

        public async Task<int> TryDelete(int id, CancellationToken token)
        {
            var affected = await setpointsRepository.DeleteAsync(id, token);
            return affected;
        }

        public IEnumerable<SetpointInfoDto> GetSetpointsNames()
            => SetpointInfos.Values;
    }

}