using FluentResults; using MediatR; using Microsoft.EntityFrameworkCore; using NexusReader.Application.Abstractions.Messaging; using NexusReader.Application.Abstractions.Services; using NexusReader.Data.Persistence; using NexusReader.Domain.Entities; namespace NexusReader.Application.Commands.Library; public class IngestEbookCommandHandler : IRequestHandler> { private readonly IDbContextFactory _dbContextFactory; private readonly IBookStorageService _storageService; public IngestEbookCommandHandler( IDbContextFactory dbContextFactory, IBookStorageService storageService) { _dbContextFactory = dbContextFactory; _storageService = storageService; } public async Task> Handle(IngestEbookCommand request, CancellationToken cancellationToken) { using var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken); string epubPath; string? coverUrl; try { // 1. Save Files epubPath = await _storageService.SaveEbookAsync(request.EpubData, $"{request.Title}.epub"); coverUrl = request.CoverImage != null && request.CoverImage.Length > 0 ? await _storageService.SaveCoverAsync(request.CoverImage, $"{request.Title}_cover.jpg") : null; } catch (Exception ex) { return Result.Fail(new Error($"Storage failure: {ex.Message}").CausedBy(ex)); } try { // 2. Resolve Author var authorName = string.IsNullOrWhiteSpace(request.AuthorName) ? "Unknown Author" : request.AuthorName.Trim(); // Use case-insensitive comparison var author = await context.Authors .FirstOrDefaultAsync(a => a.Name.ToLower() == authorName.ToLower(), cancellationToken); if (author == null) { author = new Author { Name = authorName }; context.Authors.Add(author); } // 3. Create Ebook var ebook = new Ebook { Title = request.Title, Author = author, FilePath = epubPath, // Relative URL from wwwroot CoverUrl = coverUrl, UserId = request.UserId, TenantId = request.TenantId, AddedDate = DateTime.UtcNow }; context.Ebooks.Add(ebook); await context.SaveChangesAsync(cancellationToken); return Result.Ok(ebook.Id); } catch (DbUpdateException ex) { return Result.Fail(new Error($"Database error during ingestion: {ex.Message}").CausedBy(ex)); } catch (Exception ex) { return Result.Fail(new Error($"Unexpected error during ingestion: {ex.Message}").CausedBy(ex)); } } }