using FluentResults;
using MediatR;
using Microsoft.EntityFrameworkCore;
using NexusReader.Application.Abstractions.Messaging;
using NexusReader.Data.Persistence;

namespace NexusReader.Application.Commands.Sync;

/// <summary>
/// Handles the <see cref="UpdateReadingProgressCommand"/>.
/// Persists the user's reading position and broadcasts the update to other connected devices.
/// </summary>
public class UpdateReadingProgressCommandHandler : IRequestHandler<UpdateReadingProgressCommand, Result>
{
    private readonly IDbContextFactory<AppDbContext> _dbContextFactory;
    private readonly ISyncBroadcaster _broadcaster;

    public UpdateReadingProgressCommandHandler(
        IDbContextFactory<AppDbContext> dbContextFactory,
        ISyncBroadcaster broadcaster)
    {
        _dbContextFactory = dbContextFactory;
        _broadcaster = broadcaster;
    }

    public async Task<Result> Handle(UpdateReadingProgressCommand request, CancellationToken cancellationToken)
    {
        using var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken);

        var user = await context.Users.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);
        if (user == null)
        {
            return Result.Fail("User not found.");
        }

        var now = DateTime.UtcNow;
        user.LastReadPageId = request.PageId;
        user.LastReadAt = now;

        var ebook = await context.Ebooks.FirstOrDefaultAsync(e => e.Id == request.EbookId, cancellationToken);
        if (ebook != null)
        {
            ebook.Progress = request.Progress;
            ebook.LastChapter = request.ChapterTitle;
            ebook.LastChapterIndex = request.ChapterIndex;
            ebook.LastReadDate = now;
        }

        await context.SaveChangesAsync(cancellationToken);

        // Broadcast to other devices via the abstracted broadcaster
        await _broadcaster.BroadcastProgressAsync(
            request.UserId,
            request.PageId,
            now,
            request.ExcludedConnectionId,
            cancellationToken);

        return Result.Ok();
    }
}
