using LiteCharms.Features.MidrandBooks.Abstractions; using LiteCharms.Features.MidrandBooks.Extensions; using LiteCharms.Features.MidrandBooks.Pages.Models; using LiteCharms.Features.MidrandBooks.Postgres; namespace LiteCharms.Features.MidrandBooks.Pages; public sealed class PageService(IDbContextFactory contextFactory) : IService { public async ValueTask DeleteAllAsync(long authorBookId, CancellationToken cancellationToken = default) { try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var rowsDeleted = await context.Pages .Where(p => p.AuthorBookId == authorBookId) .ExecuteDeleteAsync(cancellationToken); return rowsDeleted > 0 ? Result.Ok() : Result.Fail("No pages found for the specified book"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask DeleteByPageTypeAsync(long authorBookId, int pageNumber, BookPageTypes pageType, CancellationToken cancellationToken = default) { try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var rowsDeleted = await context.Pages .Where(p => p.AuthorBookId == authorBookId && p.Number == pageNumber && p.Type == pageType) .ExecuteDeleteAsync(cancellationToken); return rowsDeleted > 0 ? Result.Ok() : Result.Fail("Page not found"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask UpdatePageStatusAsync(long bookPageId, bool enabled, CancellationToken cancellationToken = default) { try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var rowsUpdated = await context.Pages .Where(p => p.Id == bookPageId) .ExecuteUpdateAsync(setters => setters .SetProperty(p => p.Enabled, enabled) .SetProperty(p => p.UpdatedAt, DateTime.UtcNow), cancellationToken); return rowsUpdated > 0 ? Result.Ok() : Result.Fail("Page not found"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask DeletePageAsync(long bookPageId, CancellationToken cancellationToken = default) { try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var rowsDeleted = await context.Pages .Where(p => p.Id == bookPageId) .ExecuteDeleteAsync(cancellationToken); return rowsDeleted > 0 ? Result.Ok() : Result.Fail("Page not found"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask UpdatePageAsync(long bookPageId, UpdateBookPage request, CancellationToken cancellationToken = default) { try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var rowsUpdated = await context.Pages .Where(p => p.Id == bookPageId) .ExecuteUpdateAsync(setters => setters .SetProperty(p => p.Type, request.Type) .SetProperty(p => p.ContentType, request.ContentType) .SetProperty(p => p.Number, request.Number) .SetProperty(p => p.Content, request.Content) .SetProperty(p => p.Notes, request.Notes) .SetProperty(p => p.References, request.References) .SetProperty(p => p.UpdatedAt, DateTime.UtcNow), cancellationToken); return rowsUpdated > 0 ? Result.Ok() : Result.Fail("Page not found"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> CreatePageAsync(long authorBookId, CreateBookPage request, CancellationToken cancellationToken = default) { try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.Books.AnyAsync(b => b.Id == authorBookId, cancellationToken)) return Result.Fail("Book not found"); if (await context.Pages.AnyAsync(p => p.AuthorBookId == authorBookId && p.Number == request.Number && p.Type == request.Type, cancellationToken)) return Result.Fail("A page with the same number already exists for this book"); var page = context.Pages.Add(new Entities.BookPage { AuthorBookId = authorBookId, Type = request.Type, ContentType = request.ContentType, Number = request.Number, Content = request.Content, Notes = request.Notes, References = request.References, Enabled = true }); return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok(page.Entity.Id) : Result.Fail("Failed to create page"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> GetPagesAsync(long authorBookId, CancellationToken cancellationToken = default) { try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.Books.AnyAsync(b => b.Id == authorBookId, cancellationToken)) return Result.Fail("Book not found"); var pages = await context.Pages.AsNoTracking() .Where(p => p.AuthorBookId == authorBookId).ToArrayAsync(cancellationToken); return pages?.Length > 0 ? Result.Ok(pages.Select(p => p.ToModel()).ToArray()) : Result.Fail("No pages found for the specified book"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> GetPageByNumberAsync(long pageId, int number, CancellationToken cancellationToken = default) { try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var page = await context.Pages.FirstOrDefaultAsync(p => p.Id == pageId && p.Number == number, cancellationToken); return page is not null ? page.ToModel() : Result.Fail("Page not found"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> GetPageAsync(long pageId, CancellationToken cancellationToken = default) { try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var page = await context.Pages.FirstOrDefaultAsync(p => p.Id == pageId, cancellationToken); return page is not null ? page.ToModel() : Result.Fail("Page not found"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } }