using adas_core.Domain.Enums;
using adas_core.Domain.Models.DTO;
using adas_core.Domain.Models.Filter;
using adas_core.Domain.Models.Masters;
using adas_core.Domain.Models.MongoModels;
using adas_core.Domain.Models.Responses;
using MongoDB.Bson;

namespace adas_core.Application.Services.Interfaces;

public interface IUnitService
{
    /// <summary>
    /// Asynchronously retrieves all units, with an option to include their associated POCs.
    /// </summary>
    /// <param name="withPocs">Indicates whether the returned units should include their associated POCs.</param>
    /// <returns>A task representing the asynchronous operation, containing the list of units.</returns>
    Task<List<Unit>> GetAll(bool withPocs = false);
    /// <summary>
    /// Asynchronously retrieves a compact list of all units, returning minimal summary information for each unit.
    /// </summary>
    /// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="UnitInfoDto"/> objects with the compact information of all units.</returns>
    Task<List<UnitInfoDto>> GetAllCompact();

    /// <summary>
    /// Retrieves a compact representation of a unit info by its identifier.
    /// </summary>
    /// <param name="id">The unique identifier of the unit to retrieve.</param>
    /// <returns>A task that represents the asynchronous operation, containing the compact unit information.</returns>
    Task<UnitInfoDto> GetOneCompact(ObjectId id);

    // Task<Unit?> GetByCodeSysAndCode(string unit);
    /// <summary>
    /// Asynchronously retrieves a <see cref="Unit"/> by its name.
    /// </summary>
    /// <param name="unit">The name of the unit to look up.</param>
    /// <returns>A task that represents the asynchronous operation. The task result contains the matching <see cref="Unit"/> if found; otherwise, <c>null</c>.</returns>
    Task<Unit?> GetByName(string unit);

    // Task<Unit?> GetByPointOfCare(PointOfCare pointOfCare);
    /// <summary>
    /// Retrieves information for a unit identified by the specified <paramref name="id"/>, returning <see langword="null"/> when no matching unit is found.
    /// </summary>
    /// <param name="id">The unique identifier of the unit to look up.</param>
    /// <param name="dataLocale">Optional locale used to localize the returned unit data.</param>
    /// <param name="fillLists">When <see langword="true"/>, populates the related lists on the returned unit.</param>
    /// <param name="withPoCs">When <see langword="true"/>, includes the unit's points of contact in the result.</param>
    /// <returns>A task that yields the located <see cref="Unit"/>, or <see langword="null"/> if no unit matches the given id.</returns>
    Task<Unit?> GetInfo(ObjectId id, LocaleEnum? dataLocale, bool fillLists = true, bool withPoCs = false);
    /// <summary>
    /// Retrieves information about a unit identified by the specified <paramref name="id"/>, optionally including related points of contact and devices.
    /// </summary>
    /// <param name="id">The identifier of the unit to retrieve information for.</param>
    /// <param name="withPoCs">Indicates whether related points of contact should be included in the result. Defaults to <c>true</c>.</param>
    /// <param name="withDevices">Indicates whether related devices should be included in the result. Defaults to <c>true</c>.</param>
    /// <returns>A task that represents the asynchronous operation. The task result contains the <c>Unit</c> if found, or <c>null</c> if no unit matches the specified identifier.</returns>
    Task<Unit?> GetInfo(ObjectId id, bool withPoCs = true, bool withDevices = true);

    /// <summary>
    /// Asynchronously retrieves the unit associated with the specified patient identifier, returning <c>null</c> when no matching unit exists.
    /// </summary>
    /// <param name="patientId">The unique identifier of the patient whose unit is being looked up.</param>
    /// <returns>A task that represents the asynchronous lookup, containing the matching <see cref="Unit"/> if found, or <c>null</c> if no unit is associated with the patient.</returns>
    Task<Unit?> FindByPatientId(ObjectId patientId);

    // Task<List<Unit>?> FindByLocation(PatientLocation location);
    /// <summary>
    /// Asynchronously finds and returns a <see cref="Unit"/> matching the specified identifier.
    /// </summary>
    /// <param name="id">The unique identifier of the <see cref="Unit"/> to retrieve. May be <c>null</c>.</param>
    /// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="Unit"/> if found; otherwise, <c>null</c>.</returns>
    Task<Unit?> FindById(ObjectId? id);
    /// <summary>
    /// Asynchronously retrieves a <see cref="Unit"/> entity matching the specified name.
    /// </summary>
    /// <param name="name">The name used to look up the unit. Can be null.</param>
    /// <returns>A task that resolves to the matching <see cref="Unit"/> if found; otherwise, null.</returns>
    Task<Unit?> FindByName(string? name);

    /// <summary>
    /// Asynchronously retrieves a <see cref="Unit"/> by matching either its unit name or point-of-contact (POC) name.
    /// Returns <c>null</c> if no matching unit is found.
    /// </summary>
    /// <param name="name">The unit name to search for. May be <c>null</c>.</param>
    /// <param name="pocName">The point-of-contact (POC) name to search for. May be <c>null</c>.</param>
    /// <returns>A <see cref="Task{Unit}"/> containing the matched <see cref="Unit"/>, or <c>null</c> if no unit is found.</returns>
    Task<Unit?> FindByUnitNameOrPocName(string? name, string? pocName);

    //Task<Unit?> FindByPointOfCare(PointOfCare pointOfCare);
    /// <summary>
    /// Inserts a single <see cref="Unit"/> into the underlying data store and returns the resulting entity wrapped in a task.
    /// </summary>
    /// <param name="unit">The <see cref="Unit"/> instance to be inserted.</param>
    /// <returns>A task that resolves to the inserted <see cref="Unit"/>, or <c>null</c> if the insertion did not produce a result.</returns>
    Task<Unit?> InsertOne(Unit unit);
    /// <summary>
    /// Updates the specified unit in the system.
    /// </summary>
    /// <param name="unit">The unit containing the updated information to persist.</param>
    /// <returns>A task that returns the updated <see cref="Unit"/>, or <c>null</c> if the unit was not found.</returns>
    Task<Unit?> UpdateUnit(Unit unit);
    /// <summary>
    /// Asynchronously retrieves the collection of units associated with the specified master list identifier and master list type.
    /// Returns null when no matching units are found.
    /// </summary>
    /// <param name="masterListId">The unique identifier of the master list whose units should be retrieved.</param>
    /// <param name="masterListType">The type of the master list used to scope the unit lookup.</param>
    /// <returns>A task that returns an <see cref="IEnumerable{T}"/> of <see cref="Unit"/> when matches exist, or null when no matching units are found.</returns>
    Task<IEnumerable<Unit>?> FindUnitsByMasterListId(ObjectId masterListId, MasterListType masterListType);
    /// <summary>
    /// Asynchronously counts the number of units associated with the specified master list, filtered by the given master list type.
    /// </summary>
    /// <param name="masterListId">The unique identifier of the master list whose units should be counted.</param>
    /// <param name="masterListType">The type of the master list used to scope the count to the appropriate unit category.</param>
    /// <returns>A <see cref="Task{Int64}"/> that represents the asynchronous operation, containing the total number of units matching the specified master list.</returns>
    Task<long> CountUnitsByMasterListId(ObjectId masterListId, MasterListType masterListType);
    /// <summary>
    /// Asynchronously retrieves the collection of units associated with the specified master list identifier.
    /// </summary>
    /// <param name="masterListId">The identifier of the master list whose units are to be retrieved.</param>
    /// <returns>A task that represents the asynchronous operation, containing the collection of units that belong to the specified master list.</returns>
    Task<IEnumerable<Unit>> FindUnitsByMasterListId(ObjectId masterListId);
    /// <summary>
    /// Updates the unit master list based on the provided unit ID list DTO.
    /// </summary>
    /// <param name="updateUnitListDto">The DTO containing the list of unit IDs to be used for updating the master list.</param>
    /// <returns>A task that represents the asynchronous operation. The task result contains the updated <see cref="Unit"/>, or <c>null</c> if the update was not applicable.</returns>
    Task<Unit?> UpdateUnitMasterList(UpdateUnitIdListDto updateUnitListDto);
    /// <summary>
    /// Asynchronously updates the configuration for the specified unit using the provided configuration data.
    /// </summary>
    /// <param name="unitIdParsed">The parsed identifier of the unit whose configuration will be updated.</param>
    /// <param name="unitConfiguration">The new configuration values to apply to the unit.</param>
    /// <returns>A task that resolves to <c>true</c> if the configuration was successfully updated; otherwise, <c>false</c>.</returns>
    Task<bool> UpdateConfiguration(ObjectId unitIdParsed, UnitConfiguration unitConfiguration);
    /// <summary>
    /// Asynchronously deletes a unit identified by the provided <paramref name="unit"/> entity's identifier.
    /// </summary>
    /// <param name="unit">The unit entity whose identifier is used to locate and delete the record.</param>
    /// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous operation, containing a value indicating whether the unit was successfully deleted.</returns>
    Task<bool> DeleteUnitById(Unit unit);
    /// <summary>
    /// Updates the information of an existing unit identified by <paramref name="unitId"/>, including its name, title, and optionally its configuration object ID.
    /// Returns the updated unit, or <c>null</c> when no matching unit is found.
    /// </summary>
    /// <param name="unitId">The identifier of the unit to update.</param>
    /// <param name="name">The new name to assign to the unit.</param>
    /// <param name="title">The new title to assign to the unit.</param>
    /// <param name="configObsId">An optional configuration object ID to associate with the unit.</param>
    /// <returns>A task that yields the updated <see cref="Unit"/>, or <c>null</c> if the unit does not exist.</returns>
    Task<Unit?> UpdateUnitInfo(ObjectId unitId, string name, string title, string? configObsId = null);
    /// <summary>
    /// Retrieves a paginated list of units based on the provided filter, optionally including their associated points of contact.
    /// </summary>
    /// <param name="filter">The pagination filter that defines the page size, page number, and any additional query criteria.</param>
    /// <param name="withPoCs">A flag indicating whether the response should include the points of contact associated with each unit.</param>
    /// <returns>A task that represents the asynchronous operation, containing the paginated response of units.</returns>
    Task<PaginationResponse<Unit>> GetPaginatedUnits(PaginationFilter filter, bool withPoCs);
}