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

namespace adas_core.Application.Repositories.Interfaces;

public interface IObservationRepository : IMongoRepository<PatientObservation>
{
    /// <summary>
    /// Asynchronously aggregates a patient's observations grouped by the specified field.
    /// </summary>
    /// <param name="patientId">The identifier of the patient whose observations will be aggregated.</param>
    /// <param name="groupedField">The field used to group the patient's observations during aggregation.</param>
    /// <returns>A task that represents the asynchronous operation, containing a list of BSON documents with the aggregated results.</returns>
    Task<List<BsonDocument>> AggregatedPatientGroupedObservations(ObjectId patientId, GroupedField groupedField);

    /// <summary>
    /// Asynchronously retrieves the most recent aggregated observations for a specified patient,
    /// returning up to the requested number of entries. Optionally filters the results to include
    /// only observations whose identifiers match the provided list.
    /// </summary>
    /// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
    /// <param name="num">The maximum number of most recent observations to return.</param>
    /// <param name="filterObservations">An optional list of observation identifiers used to restrict the returned results; if null, no filter is applied.</param>
    /// <returns>A task representing the asynchronous operation, containing a list of the patient's most recent <see cref="PatientObservation"/> records.</returns>
    Task<List<PatientObservation>> AggregatedPatientLastObservations(ObjectId patientId, int num,
            List<string>? filterObservations = null);

    /// <summary>
    /// Retrieves the most recent aggregated patient observations for the specified patient up to the given last date, optionally filtered by a set of observation names.
    /// </summary>
    /// <param name="patientId">The unique identifier of the patient whose observations are being retrieved.</param>
    /// <param name="num">The maximum number of observations to return.</param>
    /// <param name="lastDate">The cutoff date; only observations on or before this date are considered.</param>
    /// <param name="filterObservations">An optional list of observation names to restrict the results to. If null, no observation-name filter is applied.</param>
    /// <returns>A task that represents the asynchronous operation, containing the list of aggregated <see cref="PatientObservation"/> entries that match the criteria.</returns>
    Task<List<PatientObservation>> AggregatedPatientLastObservationsByLastDate(ObjectId patientId, int num,
            DateTime lastDate, List<string>? filterObservations = null);

    /// <summary>
    /// Retrieves the most recent patient observations, aggregated by field, for the specified patient.
    /// </summary>
    /// <param name="patientId">The unique identifier of the patient whose observations are being retrieved.</param>
    /// <param name="filterObservations">An optional list of fields to filter the observations by; if null, observations for all fields are returned.</param>
    /// <returns>A task that represents the asynchronous operation. The task result contains a list of the latest patient observations grouped by field.</returns>
    Task<List<PatientObservation>> AggregatedPatientLastObservationsByField(ObjectId patientId,
            List<Field>? filterObservations = null);

    /// <summary>
    /// Retrieves the most recent patient observations that match the specified coding system and code, returning up to the requested number of results.
    /// </summary>
    /// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
    /// <param name="codingSystem">The coding system used to classify the observations (for example, LOINC, SNOMED).</param>
    /// <param name="code">The specific code within the coding system identifying the type of observation to retrieve.</param>
    /// <param name="num">The maximum number of most recent observations to return. Defaults to 2.</param>
    /// <returns>A task that yields a collection of the matching <see cref="PatientObservation"/> records, ordered from most recent to oldest, containing at most <paramref name="num"/> entries.</returns>
    Task<IEnumerable<PatientObservation>> FindLastObservations(ObjectId patientId, string codingSystem, string code,
            int num = 2);

    /// <summary>
    /// Retrieves the most recent patient observations filtered by the specified coding system.
    /// </summary>
    /// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
    /// <param name="codingSystem">The coding system used to filter the observations.</param>
    /// <param name="num">The maximum number of recent observations to return. Defaults to 10.</param>
    /// <returns>A task that represents the asynchronous operation, containing a list of the patient's most recent observations matching the coding system.</returns>
    Task<List<PatientObservation>> FindLastObservationsByCodingSystem(ObjectId patientId, string codingSystem,
            int num = 10);

    /// <summary>
    /// Retrieves the most recent <see cref="PatientObservation"/> for the specified patient recorded before the given date, optionally filtered by observation name.
    /// </summary>
    /// <param name="patientId">The unique identifier of the patient whose observation is being queried.</param>
    /// <param name="name">The optional name of the observation to filter by. If <c>null</c>, observations of any name are considered.</param>
    /// <param name="date">The cutoff date; only observations recorded strictly before this date are eligible.</param>
    /// <returns>A task that resolves to the matching <see cref="PatientObservation"/> if found, or <c>null</c> if no observation exists before the specified date.</returns>
    Task<PatientObservation?> FindLastObservationBeforeDate(ObjectId patientId, string? name, DateTime date);

    /// <summary>
    /// Asynchronously updates the state of patient observations that have expired, applying the appropriate expiration handling to each item in the provided list.
    /// </summary>
    /// <param name="expiredObservations">The list of patient observations that have expired and require their state to be updated.</param>
    Task UpdateExpiredObservations(List<PatientObservation> expiredObservations);

    /// <summary>
    /// Finds any existing patient observations for the specified patient that share the same date, optionally filtered by observation name.
    /// </summary>
    /// <param name="patientId">The identifier of the patient whose observations are being searched.</param>
    /// <param name="name">The optional name of the observation to match; if null, observations of any name are considered.</param>
    /// <param name="date">The date used to match observations recorded on the same day.</param>
    /// <returns>A task that returns a list of matching <see cref="PatientObservation"/> instances, or <c>null</c> if no matching observations are found.</returns>
    Task<List<PatientObservation>?> FindAnyWithSameDate(ObjectId patientId, string? name, DateTime date);
    /// <summary>
    /// Asynchronously retrieves all patient observations recorded for the specified patient before the given date.
    /// </summary>
    /// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
    /// <param name="date">The cutoff date; only observations recorded prior to this date are returned.</param>
    /// <returns>A task that represents the asynchronous operation, containing a list of patient observations found before the specified date.</returns>
    Task<List<PatientObservation>> FindAnyBeforeDate(ObjectId patientId, DateTime date);

    /// <summary>
    /// Retrieves the most recent unique patient observations matching the specified name for the given patient, returning a list of <see cref="PatientObservation"/> entries.
    /// </summary>
    /// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
    /// <param name="name">The name used to match the relevant patient observations.</param>
    /// <param name="expires">An optional expiration value in seconds used to control the time window for the lookup; if <c>null</c>, no expiration is applied.</param>
    /// <returns>A task that resolves to a list of the latest unique <see cref="PatientObservation"/> records matching the specified name for the patient. The list is empty if no matching observations are found.</returns>
    Task<List<PatientObservation>> FindLatestUniqueValuesByName(ObjectId patientId, string name, int? expires);

    /// <summary>
    /// Asynchronously inserts a single <see cref="PatientObservation"/> record into the underlying data store.
    /// </summary>
    /// <param name="patientObservation">The patient observation entity to persist.</param>
    new Task InsertOneAsync(PatientObservation patientObservation);
    /// <summary>
    /// Deletes records associated with the specified patient identifier.
    /// </summary>
    /// <param name="id">The ObjectId of the patient whose related records should be removed.</param>
    Task DeleteByPatientId(ObjectId id);
    /// <summary>
    /// Asynchronously retrieves the collection of patient observations associated with the specified patient identifier.
    /// </summary>
    /// <param name="patientId">The unique identifier of the patient whose observations are to be retrieved.</param>
    /// <returns>A task that represents the asynchronous operation, containing an <see cref="IAsyncCursor{TDocument}"/> of <see cref="PatientObservation"/> instances to iterate through the matching records.</returns>
    Task<IAsyncCursor<PatientObservation>> FindByPatientIdAsync(ObjectId patientId);

    /// <summary>
    /// Asynchronously retrieves the <see cref="PatientObservation"/> records associated with the specified patient
    /// that belong to a given coding system, optionally filtered by name.
    /// </summary>
    /// <param name="patientId">The identifier of the patient whose observations are being queried.</param>
    /// <param name="codingSystem">The coding system used to classify the observations (e.g., LOINC, SNOMED).</param>
    /// <param name="name">The name of the observation to filter within the coding system.</param>
    /// <returns>A <see cref="Task{TResult}"/> that yields an <see cref="IAsyncCursor{TDocument}"/> streaming the matching <see cref="PatientObservation"/> documents.</returns>
    Task<IAsyncCursor<PatientObservation>> FindByPatientIdAndCodingSystemAsync(ObjectId patientId, string codingSystem,
            string name);

    /// <summary>
    /// Asynchronously deletes the entity identified by the specified identifier.
    /// </summary>
    /// <param name="id">The unique identifier of the entity to delete.</param>
    /// <returns>A task that represents the asynchronous delete operation.</returns>
    new Task DeleteAsync(ObjectId id);
    /// <summary>
    /// Asynchronously deletes patient observations older than the specified retention period and returns the records that were removed.
    /// </summary>
    /// <param name="name">The identifier used to locate the relevant patient observations to be evaluated for deletion.</param>
    /// <param name="retentionPolicyValue">The retention period in days; observations older than this value will be deleted.</param>
    /// <returns>A task that represents the asynchronous operation, containing a list of the deleted <see cref="PatientObservation"/> records.</returns>
    Task<List<PatientObservation>> DeleteOlderDaysAsync(string name, int retentionPolicyValue);
    /// <summary>
    /// Asynchronously deletes patient observations based on the specified name and retention policy value.
    /// </summary>
    /// <param name="name">The name used to identify or filter the patient observations to be deleted.</param>
    /// <param name="retentionPolicyValue">The retention policy threshold value that determines which older observations should be removed.</param>
    /// <returns>A task representing the asynchronous operation, containing the list of patient observations that were deleted.</returns>
    Task<List<PatientObservation>> DeleteOlderNumberAsync(string name, int retentionPolicyValue);
    /// <summary>
    /// Asynchronously checks whether a record exists for the specified patient identified by the given system identifier.
    /// </summary>
    /// <param name="patientid">The unique identifier of the patient whose record is being checked.</param>
    /// <param name="systemId">The system identifier used to look up the patient's record.</param>
    /// <returns>A task that represents the asynchronous operation. The task result is <c>true</c> if a matching record exists; otherwise, <c>false</c>.</returns>
    Task<bool> ExistBySystemId(ObjectId patientid, string systemId);
    /// <summary>
    /// Asynchronously deletes patient observations older than the specified number of seconds and returns the affected records.
    /// </summary>
    /// <param name="name">The name used to identify the patient observations to filter for deletion.</param>
    /// <param name="value">The age threshold in seconds; observations older than this value will be deleted.</param>
    /// <returns>A task that represents the asynchronous operation, containing the list of deleted patient observations.</returns>
    Task<List<PatientObservation>> DeleteOlderSecondsAsync(string name, int value);
    /// <summary>
    /// Retrieves the aggregated active intravenous lines observations for the specified patient.
    /// </summary>
    /// <param name="patientId">The unique identifier of the patient whose active intravenous lines observations are being retrieved.</param>
    /// <returns>A task that represents the asynchronous operation, containing a list of <see cref="PatientObservation"/> entries representing the patient's active intravenous lines observations, where each entry may be null.</returns>
    Task<List<PatientObservation?>> AggregatedPatientActiveIntravenousLinesObservations(ObjectId patientId);
    /// <summary>
    /// Asynchronously retrieves the most recent observation timestamp for every patient, returning a mapping of patient identifiers to their last observation times.
    /// </summary>
    /// <returns>A task that represents the asynchronous operation. The task result contains a dictionary where each key is a patient <see cref="ObjectId"/> and the associated value is the date and time of that patient's last observation.</returns>
    Task<Dictionary<ObjectId, DateTime>> FindAllLastPatientObservationTime();

    /// <summary>
    /// Asynchronously retrieves a patient observation by its unique identifier, returning <c>null</c> when no matching record is found.
    /// </summary>
    /// <param name="id">The unique identifier of the patient observation to locate.</param>
    /// <returns>A <see cref="Task{PatientObservation}"/> that resolves to the matching <see cref="PatientObservation"/>, or <c>null</c> if no observation exists for the given identifier.</returns>
    Task<PatientObservation?> FindById(ObjectId id);

    /// <summary>
    /// Asynchronously retrieves a list of patient observations associated with the specified patient identifier. Returns null when no observations are found for the patient.
    /// </summary>
    /// <param name="id">The unique identifier of the patient whose observations are being retrieved.</param>
    /// <returns>A task containing a list of <see cref="PatientObservation"/> objects for the specified patient, or null if no observations are found.</returns>
    Task<List<PatientObservation>?> FindByPatientId(ObjectId id);
    /// <summary>
    /// Updates an existing patient observation with the provided data.
    /// </summary>
    /// <param name="observation">The patient observation containing the updated information to be persisted.</param>
    Task Update(PatientObservation observation);
    /// <summary>
    /// Updates many records by replacing the specified old ObjectId with the new ObjectId identified by the given name.
    /// </summary>
    /// <param name="nameId">The name identifier used to locate the target collection or field to update.</param>
    /// <param name="id">The new ObjectId to assign to the matching records.</param>
    /// <param name="oldId">The existing ObjectId that identifies the records to be updated.</param>
    Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId);

    /// <summary>
    /// Asynchronously retrieves patient observations that have not expired, optionally filtered by the specified observation identifiers.
    /// </summary>
    /// <param name="filterObservations">An optional list of observation identifiers used to narrow the result set. May be null or contain null entries.</param>
    /// <returns>A task that resolves to a collection of non-expired <see cref="PatientObservation"/> instances matching the filter criteria.</returns>
    Task<IEnumerable<PatientObservation>> FindNotExpired(List<string?>? filterObservations);

    /// <summary>
    /// Asynchronously retrieves patient observations matching the specified name, optionally filtered by the provided date.
    /// </summary>
    /// <param name="name">The name used to look up the patient observations.</param>
    /// <param name="date">The optional date used to filter the observations; when null, results are not restricted by date.</param>
    /// <returns>A task that represents the asynchronous operation, containing a collection of <see cref="PatientObservation"/> entries that match the criteria.</returns>
    Task<IEnumerable<PatientObservation>> FindByName(string name, DateTime? date);

    /// <summary>
    /// Updates multiple <see cref="PatientObservation"/> documents that match the provided update definition.
    /// </summary>
    /// <param name="patientObservations">The collection of patient observations to update.</param>
    /// <param name="update">The update definition describing the modifications to apply to each patient observation.</param>
    Task UpdateMany(IEnumerable<PatientObservation> patientObservations, UpdateDefinition<PatientObservation> update);

    /// <summary>
    /// Retrieves all patient observation records.
    /// </summary>
    /// <returns>A task representing the asynchronous operation, containing a collection of <see cref="PatientObservation"/> entries.</returns>
    Task<IEnumerable<PatientObservation>> FindAll();

    /// <summary>
    /// Asynchronously processes and expires the specified configuration observations that have met their expiration criteria.
    /// </summary>
    /// <param name="configObservationsToExpire">The list of configuration observations to expire.</param>
    Task ExpireExpiredObservations(List<ConfigObservation> configObservationsToExpire);
    //Task<PaginationResponse<PatientObservation>> GetPaginatedObservations(PaginationFilter filter);

    /// <summary>
    /// Retrieves a paginated, fluent-queryable collection of patient observations based on the provided pagination filter.
    /// </summary>
    /// <param name="filter">The pagination filter that defines the page size, page number, and sorting criteria applied to the observations.</param>
    /// <returns>An <see cref="IFindFluent{TSource, TDocument}"/> for <see cref="PatientObservation"/> that allows further refinement and execution of the paginated query.</returns>
    IFindFluent<PatientObservation, PatientObservation> GetPaginatedObservations(PaginationFilter filter);

    /// <summary>
    /// Asynchronously retrieves the most recent non-expired patient observations matching the specified name, supporting optional pagination.
    /// </summary>
    /// <param name="patientId">The unique identifier of the patient whose observations are being queried.</param>
    /// <param name="name">The name of the observation to filter by.</param>
    /// <param name="endAfter">Optional pagination cursor indicating the number of results to skip.</param>
    /// <param name="num">Optional maximum number of observations to return.</param>
    /// <returns>A task that represents the asynchronous operation, containing a collection of matching <see cref="PatientObservation"/> objects.</returns>
    Task<IEnumerable<PatientObservation>> FindLastNotExpiredObservatonsByPatient(ObjectId patientId, string name,
            int? endAfter = null, int? num = null);
}