using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services.Cache;
using Mapster;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AsbCloudInfrastructure.Services
{
    public class SetpointsService : ISetpointsService, IConverter<SetpointsRequestDto, SetpointsRequest>
    {
        // ## Инфо от АПГ от 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 = "Скорость проработки" } },
            //{ "", new SetpointInfoDto { Name = "", DisplayName = "Скорость обратной проработки" } }, // Такая же что и прямой
            //{ "", new SetpointInfoDto { Name = "", DisplayName = "Обороты ВСП" } }, // Оно в ПЛК спинмастера, пока сделать нельзя, позднее можно.
            //{ "", new SetpointInfoDto { Name = "", DisplayName = "Расход промывочной жидкости" } }, // Нет в контроллере
        };

        private readonly CacheTable<SetpointsRequest> cacheSetpoints;
        private readonly ITelemetryService telemetryService;

        public SetpointsService(IAsbCloudDbContext db, CacheDb cacheDb, ITelemetryService telemetryService)
        {
            cacheSetpoints = cacheDb.GetCachedTable<SetpointsRequest>(
                (AsbCloudDbContext)db,
                new List<string> {
                    nameof(SetpointsRequest.Author),
                    nameof(SetpointsRequest.Well),
                });
            this.telemetryService = telemetryService;
        }

        public async Task<int> InsertAsync(SetpointsRequestDto setpoints, CancellationToken token)
        {
            setpoints.IdState = 1;
            setpoints.UploadDate = DateTime.Now;
            var inserted = await cacheSetpoints.InsertAsync(Convert(setpoints), token)
                .ConfigureAwait(false);
            return inserted?.Id ?? 0;
        }

        public async Task<IEnumerable<SetpointsRequestDto>> GetAsync(int idWell, CancellationToken token)
        {
            var entities = await cacheSetpoints.WhereAsync(s => s.IdWell == idWell, token)
                .ConfigureAwait(false);
            var dtos = entities.Select(s => Convert(s));
            return dtos;
        }

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

            if (idWell < 0)
                return null;

            var entities = (await cacheSetpoints.WhereAsync(s =>
                s.IdWell == idWell && s.IdState == 1 && s.UploadDate.AddSeconds(s.ObsolescenceSec) > DateTime.Now,
                token)
                .ConfigureAwait(false))
                .ToList();// без .ToList() работает не правильно.

            if (!entities.Any())
                return null;

            foreach (var entity in entities)
                entity.IdState = 2;
               
            await cacheSetpoints.UpsertAsync(entities, token)
                .ConfigureAwait(false);

            var dtos = entities.Select(Convert);

            return dtos;
        }

        public async Task<int> UpdateStateAsync(string uid, int id, 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.");

            var idWell = telemetryService.GetidWellByTelemetryUid(uid) ?? -1;

            if (idWell < 0)
                return 0;

            bool Predicate(SetpointsRequest s) => s.Id == id && s.IdWell == idWell && s.IdState == 2;

            var entity = await cacheSetpoints.FirstOrDefaultAsync(Predicate, token)
                .ConfigureAwait(false);

            if (entity is null)
                return 0;

            entity.IdState = setpointsRequestDto.IdState;
            await cacheSetpoints.UpsertAsync(entity, token)
                .ConfigureAwait(false);
            return 1;
        }

        public async Task<int> TryDelete(int idWell, int id, CancellationToken token)
        {
            bool Predicate(SetpointsRequest s) => s.Id == id && s.IdWell == idWell && s.IdState == 1;
            var isExist = await cacheSetpoints.ContainsAsync(Predicate, token)
                .ConfigureAwait(false);

            if (!isExist)
                return 0;

            await cacheSetpoints.RemoveAsync(Predicate, token)
                .ConfigureAwait(false);

            return 1;
        }


        public SetpointsRequest Convert(SetpointsRequestDto src)
        {
            var entity = src.Adapt<SetpointsRequest>();
            return entity;
        }

        public SetpointsRequestDto Convert(SetpointsRequest src)
        {
            var dto = src.Adapt<SetpointsRequestDto>();
            return dto;
        }

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