using AsbCloudApp.Data;
using AsbCloudApp.Data.User;
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.Any())
            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) 
            ?? throw new ArgumentInvalidException(nameof(idUser), "Пользователь не является ответственным за загрузку файла для данной категории.");
        var dto = Convert(entity);
        return dto;
    }

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