using adas_core.Application.Repositories.Interfaces;
using adas_core.Domain.Enums;
using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.DTO;
using adas_core.Domain.Models.Masters;
using adas_core.Domain.Models.MongoModels;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using Serilog;
using MongoUtils = adas_core.Infrastructure.Utils.MongoUtils;

namespace adas_core.Infrastructure.Repositories;

/// <summary>
/// Repositorio para gestionar las operaciones de la entidad Admission en MongoDB. Proporciona métodos para insertar, actualizar, eliminar y buscar admisiones, así como para manejar opciones de listas maestras relacionadas con las admisiones. 
/// Utiliza Serilog para el registro de errores y eventos importantes durante las operaciones de la base de datos.
/// </summary>
public class AdmissionRepository : MongoRepository<Admission>, IAdmissionRepository
{
    
    private readonly ApiSettings _apiSettings;

    
    public AdmissionRepository(IOptions<ApiSettings> apiSettings, IMongoDatabase database) : base(database)
    {
        if (apiSettings == null) throw new ArgumentNullException(nameof(apiSettings));
        _apiSettings = apiSettings.Value;
    }

    /// <summary>
    /// Sobrescribe el método GetCollectionName para devolver el nombre de la colección de admisiones en MongoDB, que se obtiene de la configuración de la aplicación a través de ApiSettings. 
    /// Esto permite que el repositorio se conecte a la colección correcta para realizar las operaciones de base de datos relacionadas con las admisiones.
    /// </summary>
    /// <returns>El nombre de la colección de admisiones en MongoDB.</returns>
    public override string GetCollectionName()
    {
        return _apiSettings.Admissions;
    }

    /// <summary>
    /// Sobrescribe el método InsertOneAsync para insertar una nueva admisión en la base de datos. Antes de insertar, establece la fecha de admisión (AdmissionDate) a la fecha y hora actual en formato UTC.
    /// </summary>
    /// <param name="admission">La admisión a insertar en la base de datos.</param>
    /// <returns>Una tarea que representa la operación asincrónica.</returns>
    public override async Task InsertOneAsync(Admission admission)
    {
        try
        {
            admission.AdmissionDate = DateTime.UtcNow;
            await base.InsertOneAsync(admission);
        }
        catch (Exception e)
        {
            Log.Warning("Exception trying to insert  admission: {admission}. Exception {e}", admission, e);
        }
    }

    /// <summary>
    /// Elimina una admisión de la base de datos utilizando su identificador único (ObjectId). El método construye un filtro para encontrar la admisión por su Id y luego utiliza el método DeleteOneAsync para eliminarla.
    /// Si ocurre una excepción durante el proceso, se registra un error con Serilog y se vuelve a lanzar la excepción para que pueda ser manejada por el llamador.
    /// </summary>
    /// <param name="id">El identificador único (ObjectId) de la admisión a eliminar.</param>
    /// <returns>Una tarea que representa la operación asincrónica.</returns>
    public async Task Delete(ObjectId id)
    {
        try
        {
            var filter = Builders<Admission>.Filter.Eq(x => x.Id, id);
            await Collection.DeleteOneAsync(filter, null);
        }
        catch (Exception e)
        {
            Log.Error("Exception trying to delete admission: {id}. Exception {e}", id, e);
            throw;
        }
    }

    /// <summary>
    /// Actualiza una admisión existente en la base de datos. El método recibe una instancia de Admission con los datos actualizados, construye un filtro para encontrar la admisión por su Id y luego utiliza el método UpdateOneAsync para aplicar los cambios.
    /// </summary>
    /// <param name="admission">La admisión con los datos actualizados.</param>
    /// <returns>Una tarea que representa la operación asincrónica.</returns>
    public async Task Update(Admission admission)
    {
        try
        {
            await UpdateOneAsync(admission.Id, admission);
        }
        catch (Exception e)
        {
            Log.Error("Exception trying to update admission: {admission}. Exception {e}", admission, e);
            throw;
        }
    }

    /// <summary>
    /// Actualiza la ubicación de una admisión existente en la base de datos. El método recibe el identificador único (ObjectId) de la admisión a actualizar y el nuevo identificador de la ubicación (PointOfCareId). 
    /// Construye un filtro para encontrar la admisión por su Id y luego utiliza el método UpdateOneAsync para establecer el nuevo PointOfCareId.
    /// </summary>
    /// <param name="id">El identificador único (ObjectId) de la admisión a actualizar.</param>
    /// <param name="newLocation">El nuevo identificador de la ubicación (PointOfCareId) de la admisión.</param>
    /// <returns>Una tarea que representa la operación asincrónica.</returns>
    public async Task UpdateLocation(ObjectId id, ObjectId newLocation)
    {
        var filterBuilder = Builders<Admission>.Filter;
        var filter = filterBuilder.Eq(p => p.Id, id);

        var update = Builders<Admission>.Update
            .Set(p => p.PointOfCareId, newLocation);

        await Collection.UpdateOneAsync(filter, update);
    }

    /// <summary>
    /// Actualiza los datos personales de una admisión existente en la base de datos. El método recibe el identificador único (ObjectId) de la admisión a actualizar y una instancia de Person con los nuevos datos personales.
    /// </summary>
    /// <param name="id">El identificador único (ObjectId) de la admisión a actualizar.</param>
    /// <param name="patient">La instancia de Person con los nuevos datos personales.</param>
    /// <returns>Una tarea que representa la operación asincrónica.</returns>
    public async Task UpdatePatient(ObjectId id, Person patient)
    {
        var filterBuilder = Builders<Admission>.Filter;
        var filter = filterBuilder.Eq(p => p.Id, id);

        var update = Builders<Admission>.Update
            .Set(p => p.Person, patient);

        await Collection.UpdateOneAsync(filter, update);
    }

    /// <summary>
    /// Recupera todas las admisiones almacenadas en la base de datos. El método utiliza el método FindAsync con un filtro vacío para obtener todas las admisiones y luego convierte el resultado a una lista. 
    /// Si ocurre una excepción durante el proceso, se registra un error con Serilog y se devuelve una lista vacía.
    /// </summary>
    /// <returns>Una lista de todas las admisiones almacenadas en la base de datos.</returns>
    public async Task<IEnumerable<Admission>> FindAll()
    {
        try
        {
            var result = await Collection.FindAsync(_ => true);
            return await result.ToListAsync();
        }
        catch (Exception ex)
        {
            Log.Error("Error getting all admissions. Exception: {ex}", ex);
            return [];
        }
    }

    /// <summary>
    /// Busca una admisión en la base de datos utilizando el número de paciente (patientNumber) y un identificador de unidad (unitId) distinto. 
    /// El método construye un filtro para encontrar una admisión que coincida con el número de paciente pero que tenga un unitId diferente al proporcionado.
    /// </summary>
    /// <param name="patientNumber">El número de paciente (NHC) a buscar.</param>
    /// <param name="unitId">El identificador de unidad (ObjectId) que debe ser distinto al de la admisión encontrada.</param>
    /// <returns>Una tarea que representa la operación asincrónica y devuelve la admisión encontrada o null si no se encuentra ninguna.</returns>
    public async Task<Admission?> SearchByPatientNumberAndDistinctUnit(string patientNumber, ObjectId unitId)
    {
        if (string.IsNullOrWhiteSpace(patientNumber)) return null;

        // Paciente ubicado en un PoC pero en diferente unidad
        var patient = await Collection.Find(Builders<Admission>.Filter.And(
            Builders<Admission>.Filter.Eq(p => p.Nhc, patientNumber),
            Builders<Admission>.Filter.Ne(p => p.UnitId, unitId)
        )).ToListAsync();
        // Si hay mas de una coincidencia devolvemos null por que puede no estar completo el patientNumber
        if (patient.Count > 1) return null;
        return patient.FirstOrDefault();
    }

    /// <summary>
    /// Busca una admisión en la base de datos utilizando su identificador único (ObjectId). El método construye un filtro para encontrar la admisión por su Id y luego utiliza el método FindAsync para obtenerla.
    /// </summary>
    /// <param name="id">El identificador único (ObjectId) de la admisión a buscar.</param>
    /// <returns>Una tarea que representa la operación asincrónica y devuelve la admisión encontrada o null si no se encuentra ninguna.</returns>
    public async Task<Admission?> FindById(ObjectId id)
    {
        try
        {
            var filter = Builders<Admission>.Filter.Eq(p => p.Id, id);
            var result = await Collection.FindAsync(filter);

            return await result.FirstOrDefaultAsync();
        }
        catch (Exception ex)
        {
            Log.Error("Error searching admission by id: {id}. Exception: {ex}", id, ex);
            return null;
        }
    }

    /// <summary>
    /// Busca una admisión en la base de datos utilizando el número de paciente (NHC). El método construye un filtro para encontrar la admisión por su NHC y luego utiliza el método FindAsync para obtenerla.
    /// </summary>
    /// <param name="nhc">El número de paciente (NHC) a buscar.</param>
    /// <returns>Una tarea que representa la operación asincrónica y devuelve la admisión encontrada o null si no se encuentra ninguna.</returns>
    public async Task<Admission?> FindByNhc(string nhc)
    {
        try
        {
            var filter = Builders<Admission>.Filter.Eq(p => p.Nhc, nhc);
            var result = await Collection.FindAsync(filter);

            return await result.FirstOrDefaultAsync();
        }
        catch (Exception ex)
        {
            Log.Error("Error searching admission by NHC: {nhc}. Exception: {ex}", nhc, ex);
            return null;
        }
    }

    /// <summary>
    /// Busca admisiones en la base de datos utilizando la ubicación del paciente (PatientLocation). 
    /// El método construye un filtro para encontrar admisiones que coincidan con la unidad, cama y habitación especificadas en la ubicación del paciente.
    /// </summary>
    /// <param name="location">La ubicación del paciente (PatientLocation) a buscar.</param>
    /// <returns>Una tarea que representa la operación asincrónica y devuelve una lista de admisiones que coinciden con la ubicación especificada.</returns>
    public async Task<List<Admission>> FindByLocation(PatientLocation location)
    {
        try
        {
            var filter = Builders<Admission>.Filter.Where(p =>
                p.PatientLocation != null &&
                location.UnitName == p.PatientLocation.UnitName &&
                location.Bed == p.PatientLocation.Bed &&
                location.Room == p.PatientLocation.Room);

            return await Collection.Find(filter).ToListAsync();
        }
        catch (Exception ex)
        {
            Log.Error("Error searching admission by location: {location}. Exception: {ex}", location, ex);
            return [];
        }
    }

    /// <summary>
    /// Busca admisiones en la base de datos utilizando el origen de la admisión (Origin). El método construye un filtro para encontrar admisiones que tengan un origen que coincida con el nombre del origen especificado.
    /// </summary>
    /// <param name="origin">El nombre del origen de la admisión a buscar.</param>
    /// <returns>Una tarea que representa la operación asincrónica y devuelve una lista de admisiones que coinciden con el origen especificado.</returns>
    public async Task<IEnumerable<Admission>?> FindByOrigin(string origin)
    {
        try
        {
            var filter = string.IsNullOrEmpty(origin)
                ? Builders<Admission>.Filter.Empty
                : Builders<Admission>.Filter.Where(p => p.Origin != null && origin.Equals(p.Origin.Name));

            var result = await Collection.FindAsync(filter);

            return result.ToEnumerable();
        }
        catch (Exception ex)
        {
            Log.Error("Error searching admissions by origin: {origin}. Exception: {ex}", origin, ex);
            return null;
        }
    }

    /// <summary>
    /// Inserta una nueva admisión en la base de datos y devuelve la admisión insertada. El método establece la fecha de admisión (AdmissionDate) a la fecha y hora actual en formato UTC antes de insertar la admisión.
    /// </summary>
    /// <param name="origin">La admisión a insertar.</param>
    /// <returns>Una tarea que representa la operación asincrónica y devuelve la admisión insertada o null si ocurre un error.</returns>
    public async Task<Admission?> InsertOneAsyncAndReturn(Admission origin)
    {
        try
        {
            await Collection.InsertOneAsync(origin);
            return origin;
        }
        catch (Exception ex)
        {
            Log.Error(ex.Message);
            return null;
        }
    }

    /// <summary>
    /// Busca admisiones en la base de datos utilizando el identificador de unidad (unitId) y que no tengan un PointOfCareId asignado. 
    /// El método construye un filtro para encontrar admisiones que coincidan con el unitId especificado y que tengan un PointOfCareId nulo.
    /// </summary>
    /// <param name="unitId">El identificador de la unidad a buscar.</param>
    /// <returns>Una tarea que representa la operación asincrónica y devuelve una lista de admisiones que coinciden con los criterios especificados.</returns>
    public async Task<List<Admission>> GetAdmissionByUnitIdWithOutPoC(ObjectId unitId)
    {
        try
        {
            var filter = Builders<Admission>.Filter.Where(p => p.UnitId == unitId && p.PointOfCareId == null);
            var result = await Collection.FindAsync(filter);

            return result.ToList();
        }
        catch (Exception ex)
        {
            Log.Error(ex.Message);
            return [];
        }
    }

    /// <summary>
    /// Cuenta el número de admisiones en la base de datos que coinciden con un identificador de unidad (unitId) específico. El método construye un filtro para encontrar admisiones que coincidan con el unitId especificado y luego utiliza el método CountDocumentsAsync para obtener el conteo.
    /// </summary>
    /// <param name="unitId">El identificador de la unidad a buscar.</param>
    /// <returns>Una tarea que representa la operación asincrónica y devuelve el número de admisiones que coinciden con el unitId especificado.</returns>
    public async Task<long> CountByUnitId(ObjectId unitId)
    {
        try
        {
            var filter = Builders<Admission>.Filter.Where(p => p.UnitId == unitId);
            var result = await Collection.CountDocumentsAsync(filter);
            return result;
        }
        catch (Exception ex)
        {
            Log.Error(ex.Message);
            return 0;
        }
    }

    /// <summary>
    /// Busca admisiones en la base de datos utilizando el identificador de punto de atención (PointOfCareId). 
    /// El método construye un filtro para encontrar admisiones que coincidan con el PointOfCareId especificado y luego utiliza el método FindAsync para obtenerlas.
    /// </summary>
    /// <param name="pocId">El identificador del punto de atención a buscar.</param>
    /// <returns>Una tarea que representa la operación asincrónica y devuelve una lista de admisiones que coinciden con el PointOfCareId especificado.</returns>
    public async Task<List<Admission>> FindByPointOfCareId(ObjectId pocId)
    {
        try
        {
            var filter = Builders<Admission>.Filter.Where(p => p.PointOfCareId == pocId);
            var result = await Collection.FindAsync(filter);

            return result.ToList();
        }
        catch (Exception ex)
        {
            Log.Error(ex.Message);
            return [];
        }
    }

    /// <summary>
    /// Busca admisiones en la base de datos utilizando una lista de identificadores de unidad (unitIds). El método construye un filtro para encontrar admisiones que tengan un unitId que coincida con cualquiera de los unitIds especificados y luego utiliza el método FindAsync para obtenerlas.
    /// </summary>
    /// <param name="unitIds">La lista de identificadores de unidad a buscar.</param>
    /// <returns>Una tarea que representa la operación asincrónica y devuelve una lista de admisiones que coinciden con los unitIds especificados.</returns>
    public async Task<List<Admission>> FindByUnitIds(List<ObjectId> unitIds)
    {
        var filterUnit = Builders<Admission>.Filter.In("unitId", unitIds);
        return await Collection.Find(filterUnit).ToListAsync();
    }

    /// <summary>
    /// Actualiza las opciones de la lista maestra en las admisiones que coinciden con los identificadores de unidad especificados.
    /// </summary>
    /// <param name="unitIds">La lista de identificadores de unidad a buscar.</param>
    /// <param name="opt">La opción de actualización de la lista maestra.</param>
    /// <param name="typeName">El nombre del tipo de lista maestra.</param>
    /// <returns>Una tarea que representa la operación asincrónica y devuelve una lista de admisiones actualizadas.</returns>
    public async Task<IEnumerable<Admission>> UpdateMasterListOption(List<ObjectId> unitIds,
        UpdateOptionMasterListDto opt, string typeName)
    {
        var isParsed = Enum.TryParse<MasterListType>(typeName, out var parsedTypeName);
        if (isParsed)
        {
            var filterUnit = Builders<Admission>.Filter.In("unitId", unitIds);
            switch (parsedTypeName)
            {
                case MasterListType.OriginList:
                    var originFilter = Builders<Admission>.Filter.Eq(
                        "origin.name", opt.OldOption?.Name
                    );
                    var filterUpdateorigin = Builders<Admission>.Filter.And(filterUnit, originFilter);
                    var updateorigin = Builders<Admission>.Update
                        .Set("origin.name", opt.UpdatedOption?.Name);
                    await Collection.UpdateManyAsync(filterUpdateorigin, updateorigin);

                    await Collection.UpdateManyAsync(Builders<Admission>.Filter.And(filterUnit,
                        Builders<Admission>.Filter.Eq(
                            "originAux", opt.OldOption?.Name
                        )), Builders<Admission>.Update
                        .Set("originAux", opt.UpdatedOption?.Name));

                    // Filtro para devolver los documentos actualizados (OR entre diagnosis.name y diagnosisAux)
                    var originFilterToReturn = Builders<Admission>.Filter.Eq("origin.name", opt.UpdatedOption?.Name);
                    var originAuxFilterToReturn = Builders<Admission>.Filter.Eq("originAux", opt.UpdatedOption?.Name);
                    var filterToReturnorigin = Builders<Admission>.Filter.And(
                        filterUnit,
                        Builders<Admission>.Filter.Or(originFilterToReturn, originAuxFilterToReturn)
                    );

                    // Devolver los documentos actualizados
                    var updatedDocumentsorigin = await Collection.Find(filterToReturnorigin).ToListAsync();
                    return updatedDocumentsorigin;
                case MasterListType.DiagnosisList:
                    var diagnosisFilter = Builders<Admission>.Filter.Eq(
                        "diagnosis.name", opt.OldOption?.Name
                    );
                    var filterUpdateDiagnosis = Builders<Admission>.Filter.And(filterUnit, diagnosisFilter);
                    var updateDiagnosis = Builders<Admission>.Update
                        .Set("diagnosis.name", opt.UpdatedOption?.Name)
                        .Set("diagnosis.description", opt.UpdatedOption?.Description);
                    await Collection.UpdateManyAsync(filterUpdateDiagnosis, updateDiagnosis);

                    await Collection.UpdateManyAsync(Builders<Admission>.Filter.And(filterUnit,
                        Builders<Admission>.Filter.Eq(
                            "diagnosisAux", opt.OldOption?.Name
                        )), Builders<Admission>.Update
                        .Set("diagnosisAux", opt.UpdatedOption?.Name));

                    // Filtro para devolver los documentos actualizados (OR entre diagnosis.name y diagnosisAux)
                    var diagnosisFilterToReturn =
                        Builders<Admission>.Filter.Eq("diagnosis.name", opt.UpdatedOption?.Name);
                    var diagnosisAuxFilterToReturn =
                        Builders<Admission>.Filter.Eq("diagnosisAux", opt.UpdatedOption?.Name);
                    var filterToReturn = Builders<Admission>.Filter.And(
                        filterUnit,
                        Builders<Admission>.Filter.Or(diagnosisFilterToReturn, diagnosisAuxFilterToReturn)
                    );

                    // Devolver los documentos actualizados
                    var updatedDocuments = await Collection.Find(filterToReturn).ToListAsync();
                    return updatedDocuments;
                case MasterListType.AllergyList:
                    // allergies []
                    break;
                case MasterListType.InsulationList:
                    // insulation
                    break;
                case MasterListType.LanguageBarrierList:
                    // languageBarrier
                    break;
                case MasterListType.PassiveSittingList:
                    // passiveSitting
                    break;
            }
        }

        return new List<Admission>();
    }

    /// <summary>
    /// Elimina las opciones de la lista maestra en las admisiones que coinciden con los identificadores de unidad especificados. 
    /// El método recibe una lista de identificadores de unidad, una opción de actualización de la lista maestra y el nombre del tipo de lista maestra.
    /// </summary>
    /// <param name="unitIds">La lista de identificadores de unidad a buscar.</param>
    /// <param name="opt">La opción de actualización de la lista maestra.</param>
    /// <param name="typeName">El nombre del tipo de lista maestra.</param>
    /// <returns>Una tarea que representa la operación asincrónica y devuelve una lista de admisiones actualizadas.</returns>
    public async Task<IEnumerable<Admission>> DeleteMasterListOption(List<ObjectId> unitIds, OptionList opt,
        string typeName)
    {
        var isParsed = Enum.TryParse<MasterListType>(typeName, out var parsedTypeName);
        if (isParsed)
        {
            var filterUnit = Builders<Admission>.Filter.In("unitId", unitIds);
            switch (parsedTypeName)
            {
                case MasterListType.OriginList:
                    // origin
                    // originAux
                    // Filtro para devolver los documentos actualizados (OR entre diagnosis.name y diagnosisAux)
                    var originFilterToReturn = Builders<Admission>.Filter.Eq("origin.name", opt.Name);
                    var originAuxFilterToReturn = Builders<Admission>.Filter.Eq("originAux", opt.Name);
                    var filterToReturnOrigin = Builders<Admission>.Filter.And(
                        filterUnit,
                        Builders<Admission>.Filter.Or(originFilterToReturn, originAuxFilterToReturn)
                    );

                    // Devolver los documentos actualizados
                    var updatedDocumentOrigin = await Collection.Find(filterToReturnOrigin).ToListAsync();

                    var originFilter = Builders<Admission>.Filter.Eq(
                        "origin.name", opt.Name
                    );
                    var filterUpdateOrigin = Builders<Admission>.Filter.And(filterUnit, originFilter);
                    var updateorigin = Builders<Admission>.Update
                        .Set(x => x.Origin, null);
                    await Collection.UpdateManyAsync(filterUpdateOrigin, updateorigin);

                    await Collection.UpdateManyAsync(Builders<Admission>.Filter.And(filterUnit,
                        Builders<Admission>.Filter.Eq(
                            "originAux", opt.Name
                        )), Builders<Admission>.Update
                        .Set(x => x.OriginAux, ""));


                    return updatedDocumentOrigin;

                case MasterListType.DiagnosisList:

                    // Filtro para devolver los documentos actualizados (OR entre diagnosis.name y diagnosisAux)
                    var diagnosisFilterToReturn = Builders<Admission>.Filter.Eq("diagnosis.name", opt.Name);
                    var diagnosisAuxFilterToReturn = Builders<Admission>.Filter.Eq("diagnosisAux", opt.Name);
                    var filterToReturn = Builders<Admission>.Filter.And(
                        filterUnit,
                        Builders<Admission>.Filter.Or(diagnosisFilterToReturn, diagnosisAuxFilterToReturn)
                    );

                    // Devolver los documentos actualizados
                    var updatedDocuments = await Collection.Find(filterToReturn).ToListAsync();

                    var diagnosisFilter = Builders<Admission>.Filter.Eq(
                        "diagnosis.name", opt.Name
                    );
                    var filterUpdateDiagnosis = Builders<Admission>.Filter.And(filterUnit, diagnosisFilter);
                    var updateDiagnosis = Builders<Admission>.Update
                        .Set(x => x.Diagnosis, null);
                    await Collection.UpdateManyAsync(filterUpdateDiagnosis, updateDiagnosis);

                    await Collection.UpdateManyAsync(Builders<Admission>.Filter.And(filterUnit,
                        Builders<Admission>.Filter.Eq(
                            "diagnosisAux", opt.Name
                        )), Builders<Admission>.Update
                        .Set(x => x.DiagnosisAux, null));


                    return updatedDocuments;
                case MasterListType.AllergyList:
                    // allergies []
                    break;
                case MasterListType.InsulationList:
                    // insulation
                    break;
                case MasterListType.LanguageBarrierList:
                    // languageBarrier
                    break;
                case MasterListType.PassiveSittingList:
                    // passiveSitting
                    break;
            }
        }

        return new List<Admission>();
    }

    /// <summary>
    /// Elimina todas las admisiones de la base de datos que coinciden con un identificador de unidad (unitId) específico. 
    /// El método construye un filtro para encontrar admisiones que coincidan con el unitId especificado y luego utiliza el método DeleteManyAsync para eliminarlas.
    /// </summary>
    /// <param name="unitId">El identificador de la unidad cuyas admisiones se eliminarán.</param>
    /// <returns>Una tarea que representa la operación asincrónica y devuelve un valor booleano que indica si la eliminación fue exitosa.</returns>
    public async Task<bool> DeleteAdmissionsByUnitId(ObjectId unitId)
    {
        try
        {
            var filter = Builders<Admission>.Filter.Eq(p => p.UnitId, unitId);
            await Collection.DeleteManyAsync(filter);
            return true;
        }
        catch (Exception e)
        {
            Log.Logger.Error(e.Message);
            return false;
        }
    }

    /// <summary>
    /// Crea índices en la colección de admisiones para mejorar el rendimiento de las consultas. En este caso, se crea un índice único en el campo "nhc" (número de paciente) que solo se aplica a los documentos que tienen un valor para "nhc".
    /// </summary>
    /// <returns>Una tarea que representa la operación asincrónica.</returns>
    public override async Task CreateIndexes()
    {
        var optionsUq = new CreateIndexOptions<Admission>
        {
            Background = true,
            Unique = true,
            PartialFilterExpression = Builders<Admission>.Filter.Exists(p => p.Nhc)
        };

        var indexes = new List<CreateIndexModel<Admission>>
        {
            new("{ nhc: 1 }", optionsUq)
        };

        await MongoUtils.EnsureIndexes(Collection, indexes);
    }

    /// <summary>
    /// Busca admisiones en la base de datos utilizando el diagnóstico de la admisión (Diagnosis). El método construye un filtro para encontrar admisiones que tengan un diagnóstico que coincida con el nombre del diagnóstico especificado y luego utiliza el método FindAsync para obtenerlas.
    /// </summary>
    /// <param name="diagnosis">El nombre del diagnóstico que se utilizará para buscar admisiones.</param>
    /// <returns>Una tarea que representa la operación asincrónica y devuelve una colección de admisiones que coinciden con el diagnóstico especificado.</returns>
    public async Task<IEnumerable<Admission>?> FindByDiagnosis(string diagnosis)
    {
        try
        {
            var filter =
                Builders<Admission>.Filter.Where(p => p.Diagnosis != null && diagnosis.Equals(p.Diagnosis.Name));
            var result = await Collection.FindAsync(filter);

            return result.ToEnumerable();
        }
        catch (Exception ex)
        {
            Log.Error("Error searching admissions by diagnosis: {origin}. Exception: {ex}", diagnosis, ex);
            return null;
        }
    }
}