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

namespace AsbCloudInfrastructure.Repository;

public class NotificationRepository : CrudRepositoryBase<NotificationDto, Notification>, INotificationRepository
{
   private static IQueryable<Notification> MakeQueryNotification(DbSet<Notification> dbSet)
      => dbSet.Include(n => n.NotificationCategory)
         .Include(n => n.User)
         .AsNoTracking();

   public NotificationRepository(IAsbCloudDbContext context) 
      : base(context, MakeQueryNotification)
   {
   }
   

   public async Task<PaginationContainer<NotificationDto>> GetNotificationsAsync(int idUser,
      NotificationRequest request,
      CancellationToken cancellationToken)
   {
      var skip = request.Skip ?? 0;
      var take = request.Take ?? 10;

      var query = BuildQuery(idUser, request);

      var result = new PaginationContainer<NotificationDto>
      {
         Skip = skip,
         Take = take,
         Count = await query.CountAsync(cancellationToken),
      };

      if (result.Count < skip)
         return result;

      result.Items = await query
         .SortBy(request.SortFields)
         .Skip(skip)
         .Take(take)
         .AsNoTracking()
         .Select(x => x.Adapt<NotificationDto>())
         .ToArrayAsync(cancellationToken);

      return result;
   }

   public async Task<IEnumerable<NotificationDto>> GetAllAsync(int? idUser, bool? isSent, int? idTransportType,
      CancellationToken cancellationToken)
   {
      var query = dbContext.Notifications.AsQueryable();

      if (idUser.HasValue)
         query = query.Where(n => n.IdUser == idUser);
      
      if(isSent.HasValue)
         query = isSent.Value ? 
            query.Where(n => n.SentDate != null) 
            : query.Where(n => n.SentDate == null);

      if (idTransportType.HasValue)
         query = query.Where(n => n.IdTransportType == idTransportType);
      
      return await query.AsNoTracking().Select(n => n.Adapt<NotificationDto>())
         .ToArrayAsync(cancellationToken);
   }

   public override async Task<int> UpdateRangeAsync(IEnumerable<NotificationDto> notifications, CancellationToken cancellationToken)
   {
      if (!notifications.Any())
         return 0;
      
      var ids = notifications.Select(d => d.Id);
      
      var existingEntities = await dbSet
         .Where(d => ids.Contains(d.Id))
         .AsNoTracking()
         .Select(d => d.Id)
         .ToArrayAsync(cancellationToken);

      if (ids.Count() > existingEntities.Length)
         return ICrudRepository<SetpointsRequestDto>.ErrorIdNotFound;

      var entities = notifications.Select(Convert);

      dbContext.Notifications.UpdateRange(entities);

      return await dbContext.SaveChangesAsync(cancellationToken);
   }

   public Task<int> DeleteAsync(NotificationDeleteRequest request, CancellationToken cancellationToken)
   {
      var query = dbContext.Notifications
         .Include(n => n.NotificationCategory)
         .AsQueryable();

      if (request.IdCategory.HasValue)
         query = query.Where(n => n.IdNotificationCategory == request.IdCategory.Value);

      if (request.LtSentDate.HasValue)
         query = query.Where(n => n.SentDate <= request.LtSentDate.Value.ToUniversalTime());
      
      if (request.LtReadDate.HasValue)
         query = query.Where(n => n.ReadDate <= request.LtReadDate.Value.ToUniversalTime());
      
      dbContext.Notifications.RemoveRange(query);

      return dbContext.SaveChangesAsync(cancellationToken);
   }

    public Task<int> GetUnreadCountAsync(int idUser, int idTransportType, CancellationToken cancellationToken)
    {
        return dbContext.Notifications
         .Where(n => !n.ReadDate.HasValue && 
                  n.SentDate.HasValue &&
                  n.IdUser == idUser && 
                  n.IdTransportType == idTransportType)
         .CountAsync(cancellationToken);
   }

    private IQueryable<Notification> BuildQuery(int idUser, 
      NotificationRequest request)
   {
      var query = dbContext.Notifications
         .Include(x => x.NotificationCategory)
         .Where(n => n.IdUser == idUser);

      if (request.IsSent.HasValue)
      {
         query = request.IsSent.Value ? 
            query.Where(n => n.SentDate != null) 
            : query.Where(n => n.SentDate == null);
      }

      if (request.IdTransportType.HasValue)
         query = query.Where(n => n.IdTransportType == request.IdTransportType);

      return query;
   }
}