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

namespace AsbCloudInfrastructure.Services.WellOperationService
{
    public class WellOperationService : IWellOperationService
    {
        private readonly IAsbCloudDbContext context;
        private readonly CacheTable<WellOperationCategory> cachedOperationCategories;

        public WellOperationService(IAsbCloudDbContext context, Cache.CacheDb cache)
        {
            this.context = context;
            cachedOperationCategories = cache.GetCachedTable<WellOperationCategory>((DbContext)context);
        }

        public IEnumerable<WellOperationCategoryDto> GetCategories()
        {
            var operationTypes = cachedOperationCategories
                //.Where(oc => oc.Code > 999)
                .Distinct().OrderBy(o => o.Name);
            var result = operationTypes.Adapt<WellOperationCategoryDto>();

            return result;
        }

        public async Task<PaginationContainer<WellOperationDto>> GetOperationsAsync(
            int idWell,
            int? opertaionType = default,
            IEnumerable<int> sectionTypeIds = default,
            IEnumerable<int> operationCategoryIds = default,
            DateTime begin = default,
            DateTime end = default,
            double minDepth = double.MinValue,
            double maxDepth = double.MaxValue,
            int skip = 0,
            int take = 32,
            CancellationToken token = default)
        {
            var query = context.WellOperations
                .Include(s => s.WellSectionType)
                .Include(s => s.OperationCategory)
                .Where(s => s.IdWell == idWell);

            if (opertaionType != default)
                query = query.Where(e => e.IdType == (int)opertaionType);

            if ((sectionTypeIds != default) && sectionTypeIds.Any())
                query = query.Where(e => sectionTypeIds.Contains(e.IdWellSectionType));

            if (operationCategoryIds != default && operationCategoryIds.Any())
                query = query.Where(e => operationCategoryIds.Contains(e.IdCategory));

            if (minDepth != double.MinValue)
                query = query.Where(e => e.DepthEnd >= minDepth);

            if (maxDepth != double.MaxValue)
                query = query.Where(e => e.DepthEnd <= maxDepth);

            if (begin != default)
                query = query.Where(e => e.DateStart >= begin);

            if (end != default)
                query = query.Where(e => e.DateStart <= end);

            var result = new PaginationContainer<WellOperationDto>
            {
                Skip = skip,
                Take = take,
                Count = await query.CountAsync(token).ConfigureAwait(false),
            };

            query = query
                .OrderBy(e => e.DateStart)
                .ThenBy(e => e.DepthEnd)
                .ThenBy(e => e.Id);

            if (skip > 0)
                query = query.Skip(skip);

            var entities = await query.Take(take).AsNoTracking()
                .ToListAsync(token).ConfigureAwait(false);

            foreach (var item in entities)
            {
                var dto = item.Adapt<WellOperationDto>();
                dto.WellSectionTypeName = item.WellSectionType.Caption;
                dto.CategoryName = item.OperationCategory.Name;
                result.Items.Add(dto);
            }

            return result;
        }

        public async Task<WellOperationDto> GetAsync(int id,
            CancellationToken token = default)
        {
            var entity = await context.WellOperations
                .Include(s => s.WellSectionType)
                .Include(s => s.OperationCategory)
                .FirstOrDefaultAsync(e => e.Id == id, token)
                .ConfigureAwait(false);

            if (entity is null)
                return null;

            var dto = entity.Adapt<WellOperationDto>();
            dto.WellSectionTypeName = entity.WellSectionType.Caption;
            dto.CategoryName = entity.OperationCategory.Name;
            return dto;
        }

        public async Task<int> InsertRangeAsync(int idWell,
            IEnumerable<WellOperationDto> wellOperationDtos,
            CancellationToken token = default)
        {
            foreach (var operationDto in wellOperationDtos)
            {
                var entity = operationDto.Adapt<WellOperation>();
                entity.Id = default;
                entity.IdWell = idWell;
                context.WellOperations.Add(entity);
            }

            return await context.SaveChangesAsync(token)
                .ConfigureAwait(false);
        }

        public async Task<int> UpdateAsync(int idWell, int idOperation,
            WellOperationDto item, CancellationToken token = default)
        {
            var entity = item.Adapt<WellOperation>();
            entity.Id = idOperation;
            entity.IdWell = idWell;
            context.WellOperations.Update(entity);
            return await context.SaveChangesAsync(token)
                .ConfigureAwait(false);
        }

        public async Task<int> DeleteAsync(IEnumerable<int> ids,
            CancellationToken token = default)
        {
            var query = context.WellOperations.Where(e => ids.Contains(e.Id));
            context.WellOperations.RemoveRange(query);
            return await context.SaveChangesAsync(token)
                .ConfigureAwait(false);
        }
    }
}