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 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;
	}
}