using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using QuotifyBE.Data; using QuotifyBE.Entities; using QuotifyBE.DTOs; using System.Threading.Tasks; using QuotifyBE.Mapping; using Microsoft.AspNetCore.Cors; using Microsoft.EntityFrameworkCore; namespace QuotifyBE.Controllers; [ApiController] [EnableCors] [Route("api/v1/categories")] [Produces("application/json")] public class CategoryController : ControllerBase { private readonly ApplicationDbContext _db; private readonly GeneralUseHelpers guhf; public CategoryController(ApplicationDbContext db, GeneralUseHelpers GUHF) { _db = db; guhf = GUHF; } // GET /api/v1/categories /// /// Get a category page /// /// /// Can (and will) return an empty list if no categories are found in DB.
/// Has CORS set. ///
/// The page number /// Returned on valid request /// Returned when requested page is invalid (page_no <= 0) [HttpGet("page/{page_no}")] [EnableCors] [ProducesResponseType(typeof(CategoryShortDTO), 200)] [ProducesResponseType(typeof(ErrorDTO), 404)] public async Task GetCategoryPage(int page_no = 1) { // The following seems to be a bad idea, so I leave it as is. ~eee4 // // int totalCategories = await _db.Categories.CountAsync(); // // if (totalCategories <= 0) // { // return NoContent(new ErrorDTO { Status = "error", Error_msg = "No categories to list" }); // } const int PageSize = 10; if (page_no <= 0) { return NotFound(new ErrorDTO { Status = "error", Error_msg = "Numer strony musi być większy niż 0" }); } // Get all the categories //List categories = await _db.Categories // .ToListAsync(); List categories = await _db.Categories .Skip((page_no - 1) * PageSize) .Take(PageSize) .ToListAsync(); // Convert them to a list of DTO List result = categories .Select(c => c.ToCategoryShortDTO()) .ToList(); // Return to user return Ok(result); } // POST /api/v1/categories /// /// [AUTHED] Create a new category /// /// /// Allows authorized users to create categories. ///

/// Important! /// Category names are case insensitive.
/// Has CORS set. ///
/// Returned on valid request /// Returned when such category already exists (case insensitive) [HttpPost] [Authorize] [EnableCors] [ProducesResponseType(typeof(CategoryShortDTO), 200)] [ProducesResponseType(typeof(ErrorDTO), 406)] public async Task PostNewCategory([FromBody] NewCategoryDTO formCategory) { // Check if such category doesn't already exist Category? cat = await _db.Categories.FirstOrDefaultAsync(c => c.Name.ToLower() == formCategory.Name.ToLower()); if (cat != null) { return StatusCode(406, new ErrorDTO { Status = "error", Error_msg = "This category already exists" }); } // Create new category cat = new Category { Name = formCategory.Name, Description = formCategory.Description, CreatedAt = DateTime.UtcNow }; // Add to DB await _db.Categories.AddAsync(cat); await _db.SaveChangesAsync(); // And send back to the user as DTO return Ok(cat.ToCategoryShortDTO()); } // DELETE /api/v1/categories /// /// [AUTHED] Delete a category /// /// /// Allows authorized users to delete categories. ///

/// Has CORS set. ///
/// Id of the category which shall be deleted /// Returned on valid request /// Returned when no such category exists [HttpDelete("{id}")] [Authorize] [EnableCors] [ProducesResponseType(200)] [ProducesResponseType(typeof(ErrorDTO), 404)] public async Task DeleteCategory(int id) { // (Attempt to) find the category Category? cat = await _db.Categories .FirstOrDefaultAsync(c => c.Id == id); // Failed? if (cat == null) return NotFound(new { status = "error", error_msg = "Category not found" }); // Find all the QuoteId <-> CategoryId pairs for provided id List quoteLinks = await _db.QuoteCategories .Where(qc => qc.CategoryId == id) .ToListAsync(); // For each of the dependent quotes foreach (var link in quoteLinks) { // Remove all the associative pairs _db.QuoteCategories.Remove(link); } // Finally, remove the category _db.Categories.Remove(cat); await _db.SaveChangesAsync(); // Return ok return Ok(new { Status = "ok" }); } // PATCH /api/v1/categories/1 /// /// [AUTHED] Modify an existing category /// /// /// Allows authorized users to modify categories. ///

/// Has CORS set. ///
/// Id of the category which shall be modified /// DTO with new name and description. Id and creation date are ignored. /// Returned on valid request /// Returned when category name is empty or null /// Returned when no such category exists [HttpPatch("{id}")] [Authorize] [EnableCors] [ProducesResponseType(typeof(CategoryShortDTO), 200)] [ProducesResponseType(typeof(ErrorDTO), 400)] [ProducesResponseType(typeof(ErrorDTO), 404)] public async Task EditCategory(int id, [FromBody] CategoryShortDTO updatedCategory) { // Find the category to modify Category? cat = await _db.Categories.FirstOrDefaultAsync(c => c.Id == id); // Failed? if (cat == null) return NotFound(new { status = "error", error_msg = "Category not found" }); // Otherwise, ensure the category name is not empty or null if (string.IsNullOrWhiteSpace(updatedCategory.Name)) return BadRequest(new ErrorDTO { Status = "error", Error_msg = "Category name cannot be empty." }); // Update the fields cat.Name = updatedCategory.Name; cat.Description = updatedCategory.Description; // Note the user cannot modify the createdAt field, // and we do not store last modification date. await _db.SaveChangesAsync(); // Return the modified category to user return Ok(cat.ToCategoryShortDTO()); } }