using adas_core.Application.Exceptions; using adas_core.Application.Repositories.Interfaces; using adas_core.Application.Services.Interfaces; using adas_core.Domain.Enums; using adas_core.Domain.Models.AppSettings; using adas_core.Domain.Models.Filter; using adas_core.Domain.Models.MongoModels; using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Driver; using System.Text.RegularExpressions; namespace adas_core.Infrastructure.Repositories; /// /// Repositorio para manejar las operaciones CRUD de ConfigObservation en MongoDB. /// public class ConfigObservationRepository : MongoRepository, IConfigObservationRepository { private readonly ApiSettings _apiSettings; private readonly IMasterListServiceFactory _masterListServiceFactory; /// /// Constructor para ConfigObservationRepository, inyecta las dependencias necesarias. /// /// Configuración de la API. /// Instancia de la base de datos MongoDB. /// Fábrica de servicios de lista maestra. public ConfigObservationRepository(IOptions apiSettings, IMongoDatabase database, IMasterListServiceFactory masterListServiceFactory) : base(database) { _masterListServiceFactory = masterListServiceFactory; _apiSettings = apiSettings.Value; } /// /// Obtiene el nombre de la colección de MongoDB para ConfigObservation, utilizando la configuración proporcionada o un valor predeterminado. /// /// El nombre de la colección de MongoDB para ConfigObservation. public override string GetCollectionName() { return _apiSettings.ConfigObservations ?? "config_observations"; } /// /// Busca un ConfigObservation por su ID en la base de datos MongoDB. /// /// El ID del ConfigObservation a buscar. /// El ConfigObservation encontrado, o null si no se encuentra. public async Task FindById(ObjectId id) { var result = await Collection.FindAsync(Builders.Filter.Eq(x => x.Id, id)); return await result.FirstOrDefaultAsync(); } /// /// Actualiza un ConfigObservation existente en la base de datos MongoDB. /// /// El ConfigObservation con los datos actualizados. /// El ConfigObservation actualizado, o null si no se encuentra. public async Task Update(ConfigObservation configObservation) { await UpdateOneAsync(configObservation.Id, configObservation); return configObservation; } /// /// Elimina un ConfigObservation por su ID en la base de datos MongoDB. /// /// /// public async Task Delete(ObjectId id) { return await DeleteAsync(id); } /// /// Busca todos los IDs de ConfigObservation en la base de datos MongoDB. /// /// Una lista de todos los IDs de ConfigObservation. public async Task> FindAllIds() { var allCollection = await Collection.FindAsync(_ => true); return allCollection.ToList().Select(item => item.Id).ToList(); } /// /// Busca todos los ConfigObservation en la base de datos MongoDB. /// /// Una colección de todos los ConfigObservation. public async Task> FindAll() { var result = await Collection.FindAsync(_ => true); return await result.ToListAsync(); } /// /// Busca todos los nombres de ConfigObservation en la base de datos MongoDB, eliminando duplicados y espacios en blanco. /// /// El ID del ConfigObservation para filtrar los nombres (opcional). /// Una lista de nombres de ConfigObservation. public async Task> GetConfigNames(string id) { var allConfigs = await Collection.Find(_ => true).ToListAsync(); var distinctNames = allConfigs .Where(item => !string.IsNullOrWhiteSpace(item.Name)) .Select(item => item.Name!.Trim()) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); return distinctNames; } /// /// Busca todos los nombres de ConfigObservation en la base de datos MongoDB, eliminando duplicados y espacios en blanco. /// /// public async Task> GetConfigNames() { var allConfigs = await Collection.Find(_ => true).ToListAsync(); var distinctNames = allConfigs .Where(item => !string.IsNullOrWhiteSpace(item.Name)) .Select(item => item.Name!.Trim()) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); return distinctNames; } /// /// Cuenta el número total de ConfigObservation en la base de datos MongoDB. /// /// El número total de ConfigObservation. public async Task Count() { var filter = Builders.Filter.Empty; var result = await Collection.CountDocumentsAsync(filter); return result; } /// /// Busca ConfigObservation en la base de datos MongoDB utilizando paginación y un filtro de texto opcional que busca en varios campos. /// /// El filtro de paginación y búsqueda. /// Una colección de ConfigObservation que cumple con los criterios de búsqueda y paginación. /// Se lanza cuando el texto de búsqueda es demasiado largo. public async Task> GetPaginatedItems(PaginationFilter filter) { var builder = Builders.Filter; var filterDefinition = builder.Empty; if (!string.IsNullOrWhiteSpace(filter.FilteredRequest?.Text)) { var searchText = filter.FilteredRequest.Text; if (searchText.Length > 100) throw new BadRequestException("Search text too long"); var searchTextEscaped = Regex.Escape(searchText); var regex = new BsonRegularExpression(searchTextEscaped, "i"); var searchFilters = new List> { builder.Regex(x => x.Name, regex), builder.Regex(x => x.CodingSystem, regex), builder.Regex(x => x.OriginalName, regex), builder.Regex(x => x.Code, regex), builder.Regex("uiConfiguration.screenLabel", regex) }; filterDefinition = builder.Or(searchFilters); } var sortDefinition = Builders.Sort.Ascending(x => x.Name); var skip = Math.Max(0, (filter.PageNumber - 1) * filter.PageSize); var limit = Math.Min(filter.PageSize, 100); // límite defensivo var items = await Collection .Find(filterDefinition) .Sort(sortDefinition) .Skip(skip) .Limit(limit) .ToListAsync(); return items; } /// /// Busca un ConfigObservation por su nombre en la base de datos MongoDB, utilizando una búsqueda insensible a mayúsculas y minúsculas. /// /// El nombre del ConfigObservation a buscar. /// El ConfigObservation que coincide con el nombre proporcionado, o null si no se encuentra. /// Se lanza cuando el nombre es nulo, vacío o demasiado largo. public async Task FindByName(string name) { if (string.IsNullOrWhiteSpace(name)) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat); if (name.Length > 100) throw new BadRequestException("Name too long"); var safeName = Regex.Escape(name); var filter = Builders.Filter.Regex( x => x.Name, new BsonRegularExpression($"^{safeName}$", "i") ); return await Collection .Find(filter) .FirstOrDefaultAsync(); } /// /// Busca un ConfigObservation por su sistema de codificación y código en la base de datos MongoDB, utilizando una búsqueda insensible a mayúsculas y minúsculas. /// /// El sistema de codificación del ConfigObservation a buscar. /// El código del ConfigObservation a buscar. /// El ConfigObservation que coincide con el sistema de codificación y código proporcionados, o null si no se encuentra. public async Task GetByCodeSysAndCode(string? codingSystem, string? code) { var builder = Builders.Filter; var filter = builder.Empty; if (!string.IsNullOrEmpty(codingSystem) && !string.IsNullOrEmpty(code)) { filter &= builder.Eq(x => x.CodingSystem, codingSystem); filter &= builder.Eq(x => x.Code, code); } if (filter == builder.Empty) return null; var result = await Collection.FindAsync(filter); return await result.FirstOrDefaultAsync(); } /// /// Inserta un nuevo ConfigObservation en la base de datos MongoDB y devuelve el objeto insertado, incluyendo su ID generado. /// /// El ConfigObservation a insertar en la base de datos. /// El ConfigObservation insertado, incluyendo su ID generado. public async Task InsertOneAsyncAndReturn(ConfigObservation configObservationItem) { await Collection.InsertOneAsync(configObservationItem); return configObservationItem; } /// /// Busca todos los ConfigObservation que coinciden con un nombre específico en la base de datos MongoDB, utilizando una búsqueda insensible a mayúsculas y minúsculas. /// /// El nombre del ConfigObservation a buscar. /// Una lista de ConfigObservation que coinciden con el nombre proporcionado. public async Task> FindAllByName(string name) { var builder = Builders.Filter; var filter = builder.Empty; if (!string.IsNullOrEmpty(name)) filter &= builder.Eq(x => x.Name, name); if (filter == builder.Empty) return []; var result = await Collection.FindAsync(filter); return await result.ToListAsync(); } /// /// Busca un ConfigObservation que coincida con los parámetros proporcionados (código, sistema de codificación, nombre u nombre original) en la base de datos MongoDB, utilizando una búsqueda insensible a mayúsculas y minúsculas. /// /// El código del ConfigObservation a buscar. /// El sistema de codificación del ConfigObservation a buscar. /// El nombre del ConfigObservation a buscar. /// El nombre original del ConfigObservation a buscar. /// El ConfigObservation que coincide con los parámetros proporcionados, o null si no se encuentra. public async Task GetSingleConfigObservationItem(string? code, string? codingSystem, string? name, string? originalName) { var builder = Builders.Filter; var filterDefinition = builder.Empty; if (!string.IsNullOrEmpty(codingSystem)) filterDefinition &= builder.Eq(x => x.CodingSystem, codingSystem); if (!string.IsNullOrEmpty(code)) filterDefinition &= builder.Eq(x => x.Code, code); if (!string.IsNullOrEmpty(name)) filterDefinition &= builder.Eq(x => x.Name, name); if (!string.IsNullOrEmpty(originalName)) filterDefinition &= builder.Eq(x => x.OriginalName, originalName); return await Collection .Find(filterDefinition) .FirstOrDefaultAsync(); } /// /// Inserta una carga inicial de ConfigObservation en la base de datos MongoDB utilizando una lista maestra de observaciones de enfermería. /// Si ya existen ConfigObservation en la base de datos, solo se insertan aquellos que faltan en comparación con la lista maestra. /// /// public sealed override async Task InsertInitialLoad() { var stringNurseObs = _masterListServiceFactory.StringNurseObs(); var isInitialized = await FindAll(); if (isInitialized.Count > 0) { var existingNames = isInitialized .Where(c => c.Name != null) // Filtrar nulls para evitar errores de referencia .Select(c => c.Name!) // Seleccionar solo los nombres (usamos '!' si confías en el filtro anterior) .ToList(); var missingItems = stringNurseObs .Where(x => !existingNames.Contains(x)) .ToList(); foreach (var item in missingItems) await InsertOneAsync(new ConfigObservation { Name = item, Code = "NURSE-ADAS", CodingSystem = "ADAS", InsertMode = ObservationEnum.InsertMode.Manual }); return; } var masterListObsConfig = stringNurseObs.Select(c => new ConfigObservation { Name = c, Code = "NURSE-ADAS", CodingSystem = "ADAS", InsertMode = ObservationEnum.InsertMode.Manual }) .ToList(); foreach (var item in masterListObsConfig) await InsertOneAsync(item); } }