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 class PageService(IDbContextFactory contextFactory) : IService { public async ValueTask DeleteAllAsync(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.Where(p => p.AuthorBookId == authorBookId).ToListAsync(cancellationToken); if (pages.Count == 0) return Result.Fail("No pages found for the specified book"); context.Pages.RemoveRange(pages); await context.SaveChangesAsync(cancellationToken); return Result.Ok(); } 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 page = await context.Pages.FirstOrDefaultAsync(p => p.AuthorBookId == authorBookId && p.Number == pageNumber && p.Type == pageType, cancellationToken); if (page is null) return Result.Fail("Page not found"); context.Pages.Remove(page); return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail("Failed to delete page"); } 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 page = await context.Pages.FirstOrDefaultAsync(p => p.Id == bookPageId, cancellationToken); if (page is null) return Result.Fail("Page not found"); page.UpdatedAt = DateTime.UtcNow; page.Enabled = enabled; return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail("Failed to update page status"); } 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 page = await context.Pages.FirstOrDefaultAsync(p => p.Id == bookPageId, cancellationToken); if (page is null) return Result.Fail("Page not found"); context.Pages.Remove(page); return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail("Failed to delete page"); } 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 page = await context.Pages.FirstOrDefaultAsync(p => p.Id == bookPageId, cancellationToken); if (page is null) return Result.Fail("Page not found"); page.UpdatedAt = DateTime.UtcNow; page.Type = request.Type; page.ContentType = request.ContentType; page.Number = request.Number; page.Content = request.Content; page.Notes = request.Notes; page.References = request.References; return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail("Failed to update page"); } 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)); } } }