using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AsbCloudInfrastructure.Repository
{

    public class WellFinalDocumentsRepository : IWellFinalDocumentsRepository
    {
        private readonly IAsbCloudDbContext context;
        private readonly FileService fileService;
        private readonly IUserRepository userRepository;

        public WellFinalDocumentsRepository(IAsbCloudDbContext context, 
            FileService fileService,
            IUserRepository userRepository)
        {
            this.context = context;
            this.fileService = fileService;
            this.userRepository = userRepository;
        }

        ///<inheritdoc/>
        public async Task<IEnumerable<WellFinalDocumentDBDto>> UpdateRangeAsync(int idWell, IEnumerable<WellFinalDocumentInputDto>? dtos, CancellationToken token)
        {
            if (dtos is null)
                throw new ArgumentInvalidException("Данные по категориям отсутствуют.", nameof(dtos));

            var entities = dtos
                .Where(dto => dto.IdsPublishers?.Any() == true)
                .SelectMany(dto => dto.IdsPublishers
                    .Select(idUser => new WellFinalDocument
                    {
                        IdCategory = dto.IdCategory,
                        IdWell = idWell,
                        IdUser = idUser
                    }));

            var itemsToDelete = context.WellFinalDocuments.Where(d => d.IdWell == idWell);
            context.WellFinalDocuments.RemoveRange(itemsToDelete);

            context.WellFinalDocuments.AddRange(entities);
            await context.SaveChangesAsync(token).ConfigureAwait(false);

            return entities.Adapt<IEnumerable<WellFinalDocumentDBDto>>();
        }

        ///<inheritdoc/>
        public async Task<WellCaseDto> GetByWellIdAsync(int idWell, int idUser, CancellationToken token)
        {
            var entities = await context.WellFinalDocuments
                .Include(d => d.Category)
                .Include(d => d.User)
                .Where(d => d.IdWell == idWell)
                .AsNoTracking()
                .ToArrayAsync(token)
                .ConfigureAwait(false);

            var entitiesGroups = entities
                .GroupBy(d => d.IdCategory);

            var categoriesIds = entitiesGroups
                .Select(g => g.Key);

            var files = (await fileService
                .GetInfosAsync(new FileRequest { IdWell = idWell }, token)
                .ConfigureAwait(false))
                .Where(f => categoriesIds.Contains(f.IdCategory))
                .ToArray();

            var docs = entitiesGroups.Select((g) => new WellFinalDocumentDto
            {
                IdCategory = g.Key,
                FilesCount = files
                    .Where(f => f.IdCategory == g.Key)
                    .Count(),
                File = files
                    .Where(f => f.IdCategory == g.Key)
                    .OrderBy(f => f.UploadDate)
                    .LastOrDefault(),
                NameCategory = g.First().Category.Name,
                Publishers = g.Select(i => i.User.Adapt<UserDto>()),
                PermissionToUpload = g.Any(i => i.IdUser == idUser),
            });

            var result = new WellCaseDto
            {
                IdWell = idWell,
                PermissionToSetPubliher = userRepository.HasPermission(idUser, "WellFinalDocuments.editPublisher"),
                WellFinalDocuments = docs,
            };

            return result;
        }

        ///<inheritdoc/>
        public async Task<IEnumerable<UserDto>> GetAvailableUsersAsync(int idWell, CancellationToken token)
        {
            var companyIds = await context.RelationCompaniesWells
                .AsNoTracking()
                .Where(x => x.IdWell == idWell).Select(x => x.IdCompany)
                .ToListAsync(token)
                .ConfigureAwait(false);

            var allUsers = await userRepository
                .GetAllAsync(token)
                .ConfigureAwait(false);

            return allUsers.Where(x => companyIds.Contains(x.IdCompany))
                .OrderBy(x => x.Surname)
                .Select(u => u as UserDto)
                .ToArray();
        }

        ///<inheritdoc/>
        public async Task<WellFinalDocumentDBDto> GetCategoryAsync(int idWell, int idCategory, int idUser, CancellationToken token)
        {
            var entity = await context.WellFinalDocuments
                .AsNoTracking()
                .FirstOrDefaultAsync(x => x.IdWell == idWell && x.IdCategory == idCategory && x.IdUser == idUser, token);

            if (entity is null)
                throw new ArgumentInvalidException("Пользователь не является ответственным за загрузку файла для данной категории.", nameof(entity));

            var dto = Convert(entity);
            return dto;
        }

        private static WellFinalDocumentDBDto Convert(WellFinalDocument entity)
            => entity.Adapt<WellFinalDocumentDBDto>();
    }

}