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

namespace adas_core.Application.Repositories.Interfaces;

/// <summary>
/// Repository abstraction for <see cref="Display"/> entities persisted in MongoDB.
/// </summary>
public interface IDisplayRepository : IMongoRepository<Display>
{
    /// <summary>
    /// Retrieves every display stored in the collection.
    /// </summary>
    /// <returns>A <see cref="Task{List{Display}}"/> representing the asynchronous operation. The task result contains all displays, or an empty list if none exist.</returns>
    Task<List<Display>> GetAll();
      

    /// <summary>
    /// Retrieves all displays that include the specified point-of-care in their point-of-care list.
    /// </summary>
    /// <param name="pointOfCare">The <see cref="PointOfCare"/> to filter by.</param>
    /// <returns>A <see cref="Task{List{Display}}"/> representing the asynchronous operation. The task result contains the matching displays, or an empty list if none exist.</returns>
    Task<List<Display>> GetByPointOfCare(PointOfCare pointOfCare);

    // Task<List<Display>> GetByUser();

    /// <summary>
    /// Retrieves the display whose <c>Name</c> exactly matches the supplied value.
    /// </summary>
    /// <param name="name">The display name to look up.</param>
    /// <returns>A <see cref="Task{Display}"/> representing the asynchronous operation. The task result contains the <see cref="Display"/> if found; otherwise, <see langword="null"/>.</returns>
    Task<Display?> GetByName(string name);

    /// <summary>
    /// Retrieves a display by its unique identifier.
    /// </summary>
    /// <param name="id">The <see cref="ObjectId"/> of the display to retrieve.</param>
    /// <returns>A <see cref="Task{Display}"/> representing the asynchronous operation. The task result contains the <see cref="Display"/> if found; otherwise, <see langword="null"/>.</returns>
    Task<Display?> GetById(ObjectId id);

    /// <summary>
    /// Retrieves a display by its identifier, eagerly loading its associated <see cref="DisplayConfig"/> document.
    /// </summary>
    /// <param name="id">The <see cref="ObjectId"/> of the display to retrieve.</param>
    /// <returns>A <see cref="Task{Display}"/> representing the asynchronous operation. The task result contains the <see cref="Display"/> with its config populated if found; otherwise, <see langword="null"/>.</returns>
    Task<Display?> GetByIdWithConfigDisplay(ObjectId id);

    /// <summary>
    /// Retrieves all displays that belong to the specified unit.
    /// </summary>
    /// <param name="id">The <see cref="ObjectId"/> of the unit.</param>
    /// <returns>A <see cref="Task{List{Display}}"/> representing the asynchronous operation. The task result contains the matching displays, or an empty list if none exist.</returns>
    Task<List<Display>> GetByUnitId(ObjectId id);

    /// <summary>
    /// Retrieves all displays whose <c>DisplayConfigId</c> matches the supplied value.
    /// </summary>
    /// <param name="id">The <see cref="ObjectId"/> of the display configuration to filter by.</param>
    /// <returns>A <see cref="Task{List{Display}}"/> representing the asynchronous operation. The task result contains the matching displays, or an empty list if none exist.</returns>
    Task<List<Display>> GetByConfigId(ObjectId id);

    /// <summary>
    /// Replaces the point-of-care list of the specified display with the supplied set of point-of-care identifiers.
    /// </summary>
    /// <param name="objectId">The <see cref="ObjectId"/> of the display to update.</param>
    /// <param name="listPocObId">The new list of <see cref="ObjectId"/> values representing the point-of-cares to associate with the display.</param>
    /// <returns>A <see cref="Task{Display}"/> representing the asynchronous operation. The task result contains the updated <see cref="Display"/> if the update succeeded; otherwise, <see langword="null"/>.</returns>
    Task<Display?> UpdatePointOfCareList(ObjectId objectId, List<ObjectId> listPocObId);

    /// <summary>
    /// Replaces the <see cref="DisplayConfig"/> of the display identified by <paramref name="oldDisplayId"/> with the supplied configuration instance.
    /// </summary>
    /// <param name="oldDisplayId">The <see cref="ObjectId"/> of the display whose configuration should be replaced.</param>
    /// <param name="newDisplayConfigCast">The new <see cref="DisplayConfig"/> instance to assign to the display.</param>
    /// <returns>A <see cref="Task{Display}"/> representing the asynchronous operation. The task result contains the updated <see cref="Display"/> if the update succeeded; otherwise, <see langword="null"/>.</returns>
    Task<Display?> UpdateConfig(ObjectId oldDisplayId, DisplayConfig newDisplayConfigCast);

    /// <summary>
    /// Associates the display with an existing display-config preset by setting its <c>DisplayConfigId</c> to the supplied identifier.
    /// </summary>
    /// <param name="objectIdDisplay">The <see cref="ObjectId"/> of the display to update.</param>
    /// <param name="objectIdConfigDisplay">The <see cref="ObjectId"/> of the display-config preset to associate.</param>
    /// <returns>A <see cref="Task{Display}"/> representing the asynchronous operation. The task result contains the updated <see cref="Display"/> if the update succeeded; otherwise, <see langword="null"/>.</returns>
    Task<Display?> UpdateConfigPreset(ObjectId objectIdDisplay, ObjectId objectIdConfigDisplay);

    /// <summary>
    /// Updates the <c>Name</c> of the supplied display instance and persists it.
    /// </summary>
    /// <param name="display">The <see cref="Display"/> instance to update. Its <see cref="ObjectId"/> is used to identify the document.</param>
    /// <param name="name">The new display name.</param>
    /// <returns>A <see cref="Task{Display}"/> representing the asynchronous operation. The task result contains the updated <see cref="Display"/>.</returns>
    Task<Display> UpdateName(Display display, string name);

    /// <summary>
    /// Builds a paginated, sorted, and filtered query over displays according to the supplied <see cref="PaginationFilter"/>.
    /// </summary>
    /// <param name="filter">The <see cref="PaginationFilter"/> containing pagination and filter criteria.</param>
    /// <returns>An <see cref="IFindFluent{Display, Display}"/> instance that can be used to further refine and execute the query.</returns>
    IFindFluent<Display, Display> GetPaginatedDisplays(PaginationFilter filter);

    /// <summary>
    /// Counts the number of displays that currently reference the supplied display configuration.
    /// </summary>
    /// <param name="displayConfigId">The <see cref="ObjectId"/> of the display configuration to check for usage.</param>
    /// <returns>A <see cref="Task{Int64}"/> representing the asynchronous operation. The task result contains the number of displays referencing the configuration.</returns>
    Task<long> IsDisplayConfigInUse(ObjectId displayConfigId);

    /// <summary>
    /// Updates the <c>DisplayConfigId</c> of the specified display.
    /// </summary>
    /// <param name="objectId">The <see cref="ObjectId"/> of the display to update.</param>
    /// <param name="displayConfigId">The new <see cref="ObjectId"/> of the display configuration to associate with the display.</param>
    /// <returns>A <see cref="Task{Display}"/> representing the asynchronous operation. The task result contains the updated <see cref="Display"/> if the update succeeded; otherwise, <see langword="null"/>.</returns>
    Task<Display?> UpdateConfigId(ObjectId objectId, ObjectId displayConfigId);

    /// <summary>
    /// Counts the number of displays that belong to the specified unit.
    /// </summary>
    /// <param name="unitId">The <see cref="ObjectId"/> of the unit.</param>
    /// <returns>A <see cref="Task{Int64}"/> representing the asynchronous operation. The task result contains the number of displays for the unit.</returns>
    Task<long> CountByUnitId(ObjectId unitId);

    /// <summary>
    /// Deletes every display that belongs to the specified unit.
    /// </summary>
    /// <param name="unitId">The <see cref="ObjectId"/> of the unit whose displays should be removed.</param>
    /// <returns>A <see cref="Task{Boolean}"/> representing the asynchronous operation. The task result is <see langword="true"/> when at least one display was deleted; otherwise, <see langword="false"/>.</returns>
    Task<bool> DeleteManyByUnitId(ObjectId unitId);

    /// <summary>
    /// Retrieves all displays that reference the supplied card-configuration identifier.
    /// </summary>
    /// <param name="configId">The <see cref="ObjectId"/> of the card configuration to filter by.</param>
    /// <returns>A <see cref="Task{List{Display}}"/> representing the asynchronous operation. The task result contains the matching displays, or an empty list if none exist.</returns>
    Task<List<Display>> GetByCardConfigId(ObjectId configId);
}