using LiteCharms.Features.MidrandBooks.Abstractions; using LiteCharms.Features.MidrandBooks.Authors.Models; using LiteCharms.Features.MidrandBooks.Extensions; using LiteCharms.Features.MidrandBooks.Postgres; using LiteCharms.Features.Models; namespace LiteCharms.Features.MidrandBooks.Authors; public sealed class AuthorService(IDbContextFactory contextFactory) : IService { public async ValueTask UpdateAuthorStatusAsync(long authorId, bool isEnabled, CancellationToken cancellationToken = default) { try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var rowsUpdated = await context.Authors .Where(a => a.Id == authorId) .ExecuteUpdateAsync(setters => setters .SetProperty(a => a.Enabled, isEnabled) .SetProperty(a => a.UpdatedAt, DateTime.UtcNow), cancellationToken); return rowsUpdated > 0 ? Result.Ok() : Result.Fail(new Error($"Author with ID {authorId} not found")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> GetAuthorAsync(long authorId, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var author = await context.Authors.FirstOrDefaultAsync(a => a.Id == authorId, cancellationToken); return author is not null ? Result.Ok(author.ToModel()) : Result.Fail(new Error($"Author with ID {authorId} not found")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> GetAuthorsAsync(DateRange range, CancellationToken cancellationToken = default) { try { var fromDate = range.From.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc); var toDate = range.To.ToDateTime(TimeOnly.MaxValue, DateTimeKind.Utc); await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var authors = await context.Authors.AsNoTracking() .OrderByDescending(o => o.CreatedAt) .ThenByDescending(o => o.UpdatedAt) .Where(a => a.CreatedAt >= fromDate && a.CreatedAt <= toDate) .Take(range.MaxRecords) .ToArrayAsync(cancellationToken); return authors?.Length > 0 ? Result.Ok(authors.Select(a => a.ToModel()).ToArray()) : Result.Fail(new Error("No authors found in the specified date range.")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask UpdateAuthorAsync(long authorId, UpdateAuthor request, CancellationToken cancellationToken = default) { try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var author = await context.Authors.FirstOrDefaultAsync(a => a.Id == authorId, cancellationToken); if (author is null) return Result.Fail(new Error($"Author with ID {authorId} not found")); author.UpdatedAt = DateTime.UtcNow; author.PublisherType = request.PublisherType; author.Company = request.Company; author.VatNumber = request.VatNumber; author.Name = request.Name; author.LastName = request.LastName; author.Biography = request.Biography; author.Email = request.Email; author.Website = request.Website; author.ImageUrl = request.ImageUrl; author.ThumbnailImageUrl = request.ThumbnailImageUrl; author.SocialMedia = request.SocialMedia; return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail(new Error($"Failed to update author with ID {authorId}")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> CreateAuthorAsync(CreateAuthor request, CancellationToken cancellationToken = default) { try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (await context.Authors.AnyAsync(a => a.Name == request.Name && a.LastName == request.LastName, cancellationToken)) return Result.Fail(new Error($"An author with the name {request.Name} {request.LastName} already exists")); if (await context.Authors.AnyAsync(a => a.Email == request.Email, cancellationToken)) return Result.Fail(new Error($"An author with the email {request.Email} already exists")); var newAuthor = context.Authors.Add(new Entities.Author { Company = request.Company, VatNumber = request.VatNumber, PublisherType = request.PublisherType, Name = request.Name, LastName = request.LastName, Biography = request.Biography, Email = request.Email, Website = request.Website, ImageUrl = request.ImageUrl, ThumbnailImageUrl = request.ThumbnailImageUrl, SocialMedia = request.SocialMedia }); return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok(newAuthor.Entity.Id) : Result.Fail(new Error($"Failed to create author {request.Name} {request.LastName}")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } }