using adas_core.Domain.Models;
using adas_core.Domain.Models.Filter;
using MongoDB.Bson;
using MongoDB.Driver;

namespace adas_core.Application.Repositories.Interfaces;

public interface IMedicineRepository : IMongoRepository<Medicine>
{
    /// <summary>
    /// Retrieves a medicine by its code asynchronously.
    /// </summary>
    /// <param name="code">The code used to look up the medicine.</param>
    /// <returns>A task that returns the matching <see cref="Medicine"/> if found, or <c>null</c> if no medicine matches the specified code.</returns>
    Task<Medicine?> GetMedicine(string code);

    /// <summary>
    /// Retrieves a list of medicines that match the specified codes or notes.
    /// </summary>
    /// <param name="codeNotes">The list of codes or notes used to look up the medicines.</param>
    /// <returns>A task that represents the asynchronous operation, containing the list of matching medicines.</returns>
    Task<List<Medicine>> GetMedicineByCodeOrNote(List<string> codeNotes);
    /// <summary>
    /// Retrieves a <see cref="Medicine"/> entity by its name asynchronously.
    /// </summary>
    /// <param name="name">The name of the medicine to look up.</param>
    /// <returns>A <see cref="Task{Medicine}"/> that resolves to the matching <see cref="Medicine"/>, or <c>null</c> if no medicine with the specified name is found.</returns>
    Task<Medicine?> GetMedicineByName(string name);
    /// <summary>
    /// Asynchronously retrieves all medicines from the data source.
    /// </summary>
    /// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="Medicine"/> objects; an empty list is returned when no medicines are found.</returns>
    Task<List<Medicine>> GetAll();
    /// <summary>
    /// Retrieves a medicine entity by its unique identifier from the data store.
    /// Returns <see langword="null"/> if no medicine matches the provided identifier.
    /// </summary>
    /// <param name="medicineId">The unique <see cref="ObjectId"/> of the medicine to retrieve.</param>
    /// <returns>A <see cref="Task{TResult}"/> containing the matching <see cref="Medicine"/> if found, or <see langword="null"/> when no record exists for the given identifier.</returns>
    Task<Medicine?> GetMedicineById(ObjectId medicineId);
    /// <summary>
    /// Asynchronously creates and posts a new medicine entry, returning the persisted <see cref="Medicine"/> on success or <c>null</c> when no result is produced.
    /// </summary>
    /// <param name="medicine">The medicine entity to be created and submitted.</param>
    /// <returns>A task that resolves to the newly created <see cref="Medicine"/>, or <c>null</c> if the operation does not yield a result.</returns>
    Task<Medicine?> PostMedicine(Medicine medicine);
    /// <summary>
    /// Updates an existing medicine record with the provided information.
    /// </summary>
    /// <param name="medicine">The medicine entity containing the updated data, typically identified by its primary key.</param>
    /// <returns>A task that represents the asynchronous operation. The task result contains the updated <see cref="Medicine"/>, or <c>null</c> if no matching medicine was found.</returns>
    Task<Medicine?> UpdateMedicine(Medicine medicine);
    /// <summary>
    /// Asynchronously deletes a medicine record from the data store using the specified identifier.
    /// </summary>
    /// <param name="medicineId">The unique identifier of the medicine to delete.</param>
    Task DeleteMedicineById(ObjectId medicineId);
    /// <summary>
    /// Builds a MongoDB aggregation pipeline that retrieves the distinct values for the specified field.
    /// </summary>
    /// <param name="field">The name of the field whose distinct values should be queried.</param>
    /// <returns>An <see cref="IAggregateFluent{BsonDocument}"/> representing the distinct field data query.</returns>
    IAggregateFluent<BsonDocument> GetDistinctFieldDataQuery(string field);

    /// <summary>
    /// Retrieves a paginated collection of medicines based on the specified pagination filter.
    /// </summary>
    /// <param name="filter">The pagination filter containing the page number and page size used to control the result set.</param>
    /// <returns>A fluent query interface for the paginated medicines.</returns>
    IFindFluent<Medicine, Medicine> GetPaginatedMedicines(PaginationFilter filter);
}