using DbConnect.Data; using DbConnect.Entities; using Microsoft.EntityFrameworkCore; using LibraryManagement.Shared.Models; namespace Backend.Features.Books { public interface IBookService { Task> GetAllBooksAsync(int? categoryId = null); Task GetBookByIdAsync(int id); Task> GetBooksByIdsAsync(List ids); Task CreateBookAsync(BookCreateRequest request); Task UpdateBookAsync(int id, BookUpdateRequest request); Task DeleteBookAsync(int id); } public class BookService : IBookService { private readonly AppDbContext _context; public BookService(AppDbContext context) { _context = context; } public async Task> GetAllBooksAsync(int? categoryId = null) { var query = _context.Books .Include(b => b.Categories) .Where(b => b.IsActive); if (categoryId.HasValue) { query = query.Where(b => b.Categories.Any(c => c.Id == categoryId.Value)); } var books = await query.ToListAsync(); return books.Select(b => MapToDto(b)); } public async Task GetBookByIdAsync(int id) { var book = await _context.Books .Include(b => b.Categories) .FirstOrDefaultAsync(b => b.Id == id); if (book == null || !book.IsActive) return null; return MapToDto(book); } public async Task> GetBooksByIdsAsync(List ids) { var books = await _context.Books .Include(b => b.Categories) .Where(b => ids.Contains(b.Id) && b.IsActive) .ToListAsync(); return books.Select(b => MapToDto(b)); } public async Task CreateBookAsync(BookCreateRequest request) { if (await _context.Books.AnyAsync(b => b.Isbn == request.Isbn)) { throw new Exception("A book with this ISBN already exists."); } var book = new Book { Title = request.Title, Isbn = request.Isbn, Author = request.Author, Description = request.Description, TotalCopies = request.TotalCopies, AvailableCopies = request.TotalCopies, CoverUrl = request.CoverUrl, Status = request.TotalCopies > 0 ? "Available" : "Out Of Stock", IsActive = true, CreatedAt = DateTime.UtcNow }; // Attach selected categories if (request.CategoryIds != null && request.CategoryIds.Any()) { var categories = await _context.Categories .Where(c => request.CategoryIds.Contains(c.Id)) .ToListAsync(); foreach (var cat in categories) book.Categories.Add(cat); } _context.Books.Add(book); await _context.SaveChangesAsync(); return MapToDto(book); } public async Task UpdateBookAsync(int id, BookUpdateRequest request) { var book = await _context.Books .Include(b => b.Categories) .FirstOrDefaultAsync(b => b.Id == id); if (book == null || !book.IsActive) return null; book.Title = request.Title; book.Author = request.Author; book.Description = request.Description; book.CoverUrl = request.CoverUrl; // Basic logic to sync availability when total copies change int difference = request.TotalCopies - book.TotalCopies; book.TotalCopies = request.TotalCopies; book.AvailableCopies += difference; if (book.AvailableCopies < 0) book.AvailableCopies = 0; book.Status = book.AvailableCopies > 0 ? "Available" : "Out Of Stock"; book.UpdatedAt = DateTime.UtcNow; // Sync categories: replace existing with new selection book.Categories.Clear(); if (request.CategoryIds != null && request.CategoryIds.Any()) { var categories = await _context.Categories .Where(c => request.CategoryIds.Contains(c.Id)) .ToListAsync(); foreach (var cat in categories) book.Categories.Add(cat); } await _context.SaveChangesAsync(); return MapToDto(book); } public async Task DeleteBookAsync(int id) { var book = await _context.Books.FindAsync(id); if (book == null) return false; book.IsActive = false; book.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); return true; } private static BookDto MapToDto(Book book) { return new BookDto { Id = book.Id, Title = book.Title, Isbn = book.Isbn, Author = book.Author, Status = book.Status, IsActive = book.IsActive, Description = book.Description, TotalCopies = book.TotalCopies, AvailableCopies = book.AvailableCopies, CreatedAt = book.CreatedAt, UpdatedAt = book.UpdatedAt, CoverUrl = book.CoverUrl, Categories = book.Categories.Select(c => new BookCategoryDto { Id = c.Id, Name = c.Name }).ToList() }; } } }