using adas_core.Application.Repositories.Interfaces;
using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.Filter;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using System.Text.RegularExpressions;
namespace adas_core.Infrastructure.Repositories;
///
/// Repository implementation for managing entities in MongoDB.
/// Provides CRUD operations, search, pagination, and aggregation capabilities specific to medicines.
///
public class MedicineRepository : MongoRepository, IMedicineRepository
{
private readonly ApiSettings _apiSettings;
///
/// Initializes a new instance of the class.
///
/// The application API settings containing configuration values, including the collection name. Cannot be .
/// The MongoDB database instance used to access the collection.
/// Thrown when is .
public MedicineRepository(IOptions apiSettings, IMongoDatabase database) : base(database)
{
if (apiSettings == null) throw new ArgumentNullException(nameof(apiSettings));
_apiSettings = apiSettings.Value;
} //For testing
///
/// Gets the name of the MongoDB collection used to store medicines.
/// Falls back to the default "medicines" collection name when not configured in the API settings.
///
/// The collection name retrieved from the API settings, or "medicines" if not configured.
public override string GetCollectionName()
{
return _apiSettings.Medicines ?? "medicines";
}
///
/// Asynchronously retrieves a medicine whose Codes or Notes collection contains the specified code.
///
/// The code or note text to search for within the medicine's codes or notes.
///
/// A representing the asynchronous operation.
/// The task result contains the first matching the criteria, or if no match is found.
///
public async Task GetMedicine(string code)
{
var result = await Collection.FindAsync(x => x.Codes.Contains(code) || x.Notes.Contains(code));
return await result.FirstOrDefaultAsync();
}
///
/// Asynchronously retrieves all medicines whose Codes array contains any of the specified code values.
///
/// A list of code strings to match against the medicine's codes. At least one code must match.
///
/// A representing the asynchronous operation.
/// The task result contains a list of matching objects. Returns an empty list if no matches are found.
///
public async Task> GetMedicineByCodeOrNote(List codeNotes)
{
var filter = Builders.Filter.AnyIn("Codes", codeNotes.ToArray());
var result = await Collection.FindAsync(filter);
return result.ToList();
}
///
/// Asynchronously retrieves a medicine by its exact name.
///
/// The exact name of the medicine to search for.
///
/// A representing the asynchronous operation.
/// The task result contains the if found; otherwise, .
///
public async Task GetMedicineByName(string name)
{
var filter = Builders.Filter.Eq(p => p.Name, name);
var result = await Collection.FindAsync(filter);
return await result.FirstOrDefaultAsync();
}
///
/// Asynchronously retrieves all medicines stored in the collection.
///
///
/// A representing the asynchronous operation.
/// The task result contains a list of all objects. Returns an empty list if the collection is empty.
///
public async Task> GetAll()
{
var result = await Collection.FindAsync(_ => true);
return result.ToList();
}
///
/// Asynchronously retrieves a medicine by its unique identifier.
///
/// The of the medicine to retrieve.
///
/// A representing the asynchronous operation.
/// The task result contains the if found; otherwise, .
///
public async Task GetMedicineById(ObjectId medicineId)
{
var filter = Builders.Filter.Eq(p => p.Id, medicineId);
var result = await Collection.FindAsync(filter);
return await result.FirstOrDefaultAsync();
}
///
/// Asynchronously inserts a new medicine into the database and returns the inserted entity (looked up by name).
///
/// The instance to insert.
///
/// A representing the asynchronous operation.
/// The task result contains the newly inserted retrieved by its name, or if the lookup fails.
///
public async Task PostMedicine(Medicine medicine)
{
await Collection.InsertOneAsync(medicine);
var result = await Collection.FindAsync(v => v.Name == medicine.Name);
return await result.FirstOrDefaultAsync();
}
///
/// Asynchronously updates an existing medicine in the database and returns the updated entity.
///
/// The instance containing the updated values. The is used to identify the document.
///
/// A representing the asynchronous operation.
/// The task result contains the same instance that was passed in, after the update operation has been issued.
///
public async Task UpdateMedicine(Medicine medicine)
{
await UpdateOneAsync(medicine.Id, medicine);
return medicine;
}
///
/// Asynchronously deletes a medicine from the database by its unique identifier.
///
/// The of the medicine to delete.
/// A representing the asynchronous delete operation.
public async Task DeleteMedicineById(ObjectId medicineId)
{
var filter = Builders.Filter.Eq(po => po.Id, medicineId);
await Collection.DeleteOneAsync(filter);
}
///
/// Retrieves a paginated, sorted, and filtered set of medicines based on the provided pagination filter.
/// Results are sorted ascending by name. Supports optional case-insensitive regex match on the medicine name,
/// and exact matches on code, type, and group.
///
/// The containing pagination and filtering criteria.
///
/// An instance that can be used to further refine and execute the query.
/// When no filters are provided, all medicines are returned sorted by name.
///
public IFindFluent GetPaginatedMedicines(PaginationFilter filter)
{
// Crear variable con la clase que construye los filtros que necesitamos
var filterBuilder = Builders.Filter;
// Crear una lista de filtros que pueden venir de tu servicio
var filters = new List>();
// Ordenar los resultados por "time" en orden descendente
var sort = Builders.Sort.Ascending("name");
if (filter.FilteredRequest != null)
{
var requestFilter = filter.FilteredRequest;
if (!string.IsNullOrWhiteSpace(requestFilter.MedicineName))
{
var escapedTextFilter = Regex.Escape(requestFilter.MedicineName);
filters.Add(filterBuilder.Regex(m => m.Name,
new BsonRegularExpression(escapedTextFilter, "i")));
}
if (!string.IsNullOrWhiteSpace(requestFilter.MedicineCode))
filters.Add(filterBuilder.AnyEq(m => m.Codes, requestFilter.MedicineCode));
if (!string.IsNullOrWhiteSpace(requestFilter.MedicineType))
filters.Add(filterBuilder.AnyEq(m => m.Type, requestFilter.MedicineType));
if (!string.IsNullOrWhiteSpace(requestFilter.MedicineGroup))
filters.Add(filterBuilder.AnyEq(m => m.Group, requestFilter.MedicineGroup));
}
if (filters.Count == 0)
return Collection.Find(_ => true).Sort(sort);
var combinedFilter = Builders.Filter.And(filters);
return Collection
.Find(combinedFilter)
.Sort(sort);
}
///
/// Builds an aggregation pipeline that retrieves the distinct values of a specified array field
/// from all medicines. The pipeline unwinds the field, groups by its value, sorts alphabetically,
/// and projects the result with the original field name.
///
/// The name of the array field on the document to retrieve distinct values for (for example, "Codes", "Type", or "Group").
///
/// An representing the aggregation pipeline that, when executed,
/// yields documents containing the distinct values of the specified field.
///
public IAggregateFluent GetDistinctFieldDataQuery(string field)
{
return Collection.Aggregate()
.Unwind(field)
.Group(new BsonDocument { { "_id", $"${field}" } })
.Sort(new BsonDocument { { "_id", 1 } })
.Project(new BsonDocument { { field, "$_id" }, { "_id", 0 } });
}
///
/// Retrieves all distinct medicine groups available in the collection.
///
///
/// A representing the asynchronous operation,
/// containing a list of distinct group names as strings.
///
/// This method is not yet implemented.
public Task> GetAllGroups()
{
throw new NotImplementedException();
}
}