using MediatR;
using Microsoft.EntityFrameworkCore;
using SolutionErp.Application.Common.Exceptions;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Application.Notifications.Dtos;
using SolutionErp.Domain.Notifications;

namespace SolutionErp.Application.Notifications;

// ========== LIST current-user's notifications ==========

public record ListMyNotificationsQuery(bool UnreadOnly, int Limit) : IRequest<IReadOnlyList<NotificationDto>>;

public class ListMyNotificationsQueryHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser) : IRequestHandler<ListMyNotificationsQuery, IReadOnlyList<NotificationDto>>
{
    public async Task<IReadOnlyList<NotificationDto>> Handle(ListMyNotificationsQuery request, CancellationToken ct)
    {
        var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
        var take = request.Limit is > 0 and <= 200 ? request.Limit : 50;

        var q = db.Notifications.AsNoTracking().Where(n => n.UserId == userId);
        if (request.UnreadOnly) q = q.Where(n => n.ReadAt == null);

        return await q
            .OrderByDescending(n => n.CreatedAt)
            .Take(take)
            .Select(n => new NotificationDto(
                n.Id,
                (int)n.Type,
                n.Title,
                n.Description,
                n.Href,
                n.RefId,
                n.CreatedAt,
                n.ReadAt))
            .ToListAsync(ct);
    }
}

// ========== UNREAD count ==========

public record GetMyUnreadCountQuery : IRequest<int>;

public class GetMyUnreadCountQueryHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser) : IRequestHandler<GetMyUnreadCountQuery, int>
{
    public async Task<int> Handle(GetMyUnreadCountQuery request, CancellationToken ct)
    {
        var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
        return await db.Notifications.CountAsync(n => n.UserId == userId && n.ReadAt == null, ct);
    }
}

// ========== MARK as read ==========

public record MarkNotificationReadCommand(Guid Id) : IRequest;

public class MarkNotificationReadCommandHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser,
    IDateTime clock) : IRequestHandler<MarkNotificationReadCommand>
{
    public async Task Handle(MarkNotificationReadCommand request, CancellationToken ct)
    {
        var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
        var entity = await db.Notifications.FirstOrDefaultAsync(n => n.Id == request.Id && n.UserId == userId, ct)
            ?? throw new NotFoundException("Notification", request.Id);

        if (entity.ReadAt == null)
        {
            entity.ReadAt = clock.UtcNow;
            await db.SaveChangesAsync(ct);
        }
    }
}

// ========== MARK all as read ==========

public record MarkAllNotificationsReadCommand : IRequest<int>;

public class MarkAllNotificationsReadCommandHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser,
    IDateTime clock) : IRequestHandler<MarkAllNotificationsReadCommand, int>
{
    public async Task<int> Handle(MarkAllNotificationsReadCommand request, CancellationToken ct)
    {
        var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
        var now = clock.UtcNow;
        var unread = await db.Notifications
            .Where(n => n.UserId == userId && n.ReadAt == null)
            .ToListAsync(ct);
        foreach (var n in unread) n.ReadAt = now;
        if (unread.Count > 0) await db.SaveChangesAsync(ct);
        return unread.Count;
    }
}
