Clear        


                
                    using APP.Domain;
using APP.Models;
using CORE.APP.Models;
using CORE.APP.Services;

namespace APP.Services
{
    /// <summary>
    /// Provides category-related CRUD operations business logic for the application.
    /// Inherits from <see cref="ServiceBase"/> to utilize culture with success and error response helper methods.
    /// Temporary DbContext operations demonstration class, will be replaced with CategoryService in the future.
    /// </summary>
    [Obsolete("Use CategoryService class instead!")]
    public class CategoryObsoleteService : ServiceBase
    {
        private readonly Db _db;

        /// <summary>
        /// Initializes a new instance of the <see cref="CategoryObsoleteService"/> class with the specified database context.
        /// </summary>
        /// <param name="db">The database context used for accessing category data.</param>
        public CategoryObsoleteService(Db db)
        {
            _db = db;
        }

        /// <summary>
        /// Returns a queryable collection (query) of <see cref="CategoryResponse"/> model (DTO) objects representing categories.
        /// </summary>
        /// <returns>
        /// An <see cref="IQueryable{T}"/> of <see cref="CategoryResponse"/> for further filtering or enumeration.
        /// </returns>
        public IQueryable<CategoryResponse> Query()
        {
            // Project each Category entity from the Categories table in the database into a CategoryResponse response model (DTO).
            // Here, projection means mapping the values of the entity properties to the corresponding properties of the response model.

            // Way 1: types can be used with variables for declarations
            //IQueryable<CategoryResponse> query = _db.Categories.Select(categoryEntity => new CategoryResponse()
            // Way 2: var can also be used therefore the type of the variable (IQueryable<CategoryResponse>) will be known dynamically
            // if an assignment is provided, if no assignment, types must be used
            var query = _db.Categories.Select(categoryEntity => new CategoryResponse()
            {
                Id = categoryEntity.Id,                     // Map the unique integer identifier.
                Guid = categoryEntity.Guid,                 // Map the unique string identifier.
                Title = categoryEntity.Title,               // Map the category title.
                Description = categoryEntity.Description    // Map the category description.
            });

            // Return the queryable result for filtering (e.g. getting a single item) or enumeration (e.g. getting a list of items).
            return query;
        }

        /// <summary>
        /// Retrieves the category entity with the specified unique identifier from the database
        /// and maps its data to a <see cref="CategoryRequest"/> model (DTO) for editing.
        /// If the category is not found, returns <c>null</c>.
        /// </summary>
        /// <param name="id">The unique identifier of the category to edit.</param>
        /// <returns>
        /// A <see cref="CategoryRequest"/> containing the category's data if found; otherwise, <c>null</c>.
        /// </returns>
        public CategoryRequest Edit(int id)
        {
            // Attempt to find the Category entity by its ID in the database.
            var entity = _db.Categories.Find(id);

            // If the entity is not found, return null to indicate the category does not exist.
            if (entity is null)
                return null;

            // Map the found entity's properties to a new CategoryRequest model.
            var request = new CategoryRequest()
            {
                Id = entity.Id,                  // Set the category's unique identifier.
                Title = entity.Title,            // Set the category's title.
                Description = entity.Description // Set the category's description.
            };

            // Return the populated CategoryRequest model for editing.
            return request;
        }

        /// <summary>
        /// Creates a new category in the database using the provided <see cref="CategoryRequest"/> model (DTO).
        /// Validates that no existing category has the same title (case-sensitive, trimmed).
        /// If a duplicate title exists, returns an error <see cref="CommandResponse"/>.
        /// Otherwise, adds the new category, saves changes, and returns a success <see cref="CommandResponse"/> containing the created category's ID.
        /// </summary>
        /// <param name="request">The category data to create.</param>
        /// <returns>
        /// A <see cref="CommandResponse"/> indicating the result of the operation: success with the new category's ID if created, 
        /// or error if a duplicate title exists.
        /// </returns>
        public CommandResponse Create(CategoryRequest request)
        {
            // Check if any category already exists with the same title (case-sensitive, trimmed) preventing duplicate category titles in the database.
            // Way 1:
            //var existingEntity = _db.Categories.SingleOrDefault(categoryEntity => categoryEntity.Title == request.Title.Trim());
            //if (existingEntity is not null) // if (existingEntity != null) can alo be written
            //    return Error("Category with the same title exists!");
            // Way 2:
            if (_db.Categories.Any(categoryEntity => categoryEntity.Title == request.Title.Trim()))
                return Error("Category with the same title exists!");

            // Creates a new Category entity with the provided title (trimmed for consistency).
            var entity = new Category()
            {
                Guid = Guid.NewGuid().ToString(), // generates a new unique string identifier for the category
                Title = request.Title.Trim(), // since request.Title has required data annotation and can't be null,
                                              // assign request.Title's trimmed value (?. won't have any effect but can be used)
                Description = request.Description?.Trim() // since request.Description is optional and can be null,
                                                          // use null-conditional operator (?.) to avoid Null Reference Exception
            };

            // Adds the new Category entity to the database context.
            _db.Categories.Add(entity); // _db.Add(entity); can also be written

            // Saves changes to the database by using Unit of Work (all changes made to the DbSets will be commited to the database once).
            _db.SaveChanges();

            // Returns a success response indicating the category was created with the created category entity's Id value.
            return Success("Category created successfully.", entity.Id);

            // There are also asynchronous versions of methods such as SingleOrDefaultAsync, AnyAsync and SaveChangesAsync
            // that can be used with await in async methods.

            /* Some LINQ (Language Integrated Query) methods for querying data (async versions already exists):
            Find: Finds an entity with the given primary key value. Returns null if not found. 
            Uses the database context's cache before querying the database.
            Example: var group = _db.Groups.Find(5);
            
            Single: Returns the only element that matches the specified condition(s).
            Throws an exception if no element or more than one element is found.
            Example: var group = _db.Groups.Single(groupEntity => groupEntity.Id == 5);
            
            SingleOrDefault: Returns the only element that matches the specified condition(s), or null if no such element exists.
            Throws an exception if more than one element is found.
            Example: var group = _db.Groups.SingleOrDefault(groupEntity => groupEntity.Id == 5);
            
            First: Returns the first element that matches the specified condition(s).
            Throws an exception if no element is found.
            Example: var group = _db.Groups.First();
            Example: var group = _db.Groups.First(groupEntity => groupEntity.Id > 5 && groupEntity.Title.StartsWith("Jun");
            
            FirstOrDefault: Returns the first element that matches the specified condition(s), or null if no such element exists.
            Example: var group = _db.Groups.FirstOrDefault();
            Example: var group = _db.Groups.FirstOrDefault(groupEntity => groupEntity.Id < 5 || groupEntity.Title == "Senior");
            
            Last: Returns the last element that matches the specified condition(s).
            Throws an exception if no element is found. Usually requires an OrderBy or OrderByDescending clause.
            Example: var group = _db.Groups.OrderByDescending(groupEntity => groupEntity.Id).Last(); 
            gets the first group from the groups descending ordered by Id.
            Example: var group = _db.Groups.OrderBy(groupEntity => groupEntity.Id).Last();
            gets the last group from the groups ordered by Id.
            
            LastOrDefault: Returns the last element that matches the specified condition(s), or null if no such element exists.
            Usually requires an OrderBy or OrderByDescending clause.
            Example: var group = _db.Groups.OrderBy(groupEntity => groupEntity.Id).LastOrDefault();
            Example: var group = _db.Groups.OrderBy(groupEntity => groupEntity.Id).LastOrDefault(groupEntity.Title.Contains("io"));

            Where: Returns the filtered query that matches the specified condition(s). Tolist, SingleOrDefault or FirstOrDefault 
            methods are invoked to get the filtered data.
            Example: var groups = _db.Groups.Where(groupEntity => groupEntity.Id > 5).ToList();

            Note: SingleOrDefault is generally preferred to get single data.
            Note: These LINQ methods can also be used with collections such as lists and arrays.
            */
        }

        /// <summary>
        /// Updates an existing category in the database using the provided <see cref="CategoryRequest"/> model (DTO).
        /// Validates that no existing category has the same title other than the current updated category (case-sensitive, trimmed).
        /// If a duplicate title exists, returns an error <see cref="CommandResponse"/>.
        /// Otherwise, updates the category, saves changes, and returns a success <see cref="CommandResponse"/> containing the updated category's ID.
        /// </summary>
        /// <param name="request">The category data to update.</param>
        /// <returns>
        /// A <see cref="CommandResponse"/> indicating the result of the operation: success with the existing category's ID if updated, 
        /// or error if a duplicate title exists.
        /// </returns>
        public CommandResponse Update(CategoryRequest request)
        {
            // If any other category (excluding the current one) already has the same title (case-sensitive, trimmed), don't update.
            if (_db.Categories.Any(categoryEntity => categoryEntity.Id != request.Id && categoryEntity.Title == request.Title.Trim()))
                return Error("Category with the same title exists!");

            // Attempt to find the Category entity by its ID, if not found return error command response with message.
            var entity = _db.Categories.Find(request.Id);
            if (entity is null)
                return Error("Category not found!");

            // Update the Category entity's title and description with the new values (trimmed for consistency).
            entity.Title = request.Title?.Trim(); // since request.Title has required data annotation and can't be null,
                                                  // assign request.Title's trimmed value (?. won't have any effect but can be used)
            entity.Description = request.Description?.Trim(); // since request.Description is optional and can be null,
                                                              // use null-conditional operator (?.) to avoid Null Reference Exception

            // Mark the entity as modified in the context.
            _db.Categories.Update(entity); // _db.Update(entity); can also be written

            // Persist the changes to the database by using Unit of Work (all changes made to the DbSets will be commited to the database once).
            _db.SaveChanges();

            // Return a success response indicating the category was updated, including the entity's ID.
            return Success("Category updated successfully.", entity.Id);
        }

        /// <summary>
        /// Deletes the category with the specified unique identifier from the database.
        /// If the category is not found, returns an error <see cref="CommandResponse"/>.
        /// Otherwise, removes the category, saves changes, and returns a success <see cref="CommandResponse"/>
        /// containing the deleted category's ID.
        /// </summary>
        /// <param name="id">The unique identifier of the category to delete.</param>
        /// <returns>
        /// A <see cref="CommandResponse"/> indicating the result of the operation:
        /// success with the deleted category's ID if found and deleted, or error if the category is not found.
        /// </returns>
        public CommandResponse Delete(int id)
        {
            var entity = _db.Categories.Find(id);

            // If the category does not exist, return an error command response.
            if (entity is null)
                return Error("Category not found!");

            // Remove the Category entity from the database context.
            _db.Categories.Remove(entity); // _db.Remove(entity); can also be written

            // Persist the changes to the database by using Unit of Work (all changes made to the DbSets will be commited to the database once).
            _db.SaveChanges();

            // Return a success response indicating the category was deleted with the deleted catergory entity's ID value.
            return Success("Category deleted successfully.", entity.Id);
        }
    }
}