using adas_core.Application.Repositories.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 Newtonsoft.Json; using Serilog; using System.Text.RegularExpressions; namespace adas_core.Infrastructure.Repositories; /// /// Represents a repository for managing entities, providing MongoDB-backed data access through the base class and exposing the contract defined by . /// /// The type of the entity managed by the repository. /// This class combines a concrete MongoDB repository implementation with a domain-specific interface, enabling standardized persistence operations for relay entities. public class RelayRepository : MongoRepository, IRelayRepository { private readonly ApiSettings _apiSettings; public RelayRepository(IMongoDatabase database, IOptions apiSettings) : base(database) { _apiSettings = apiSettings.Value; } /// /// Retrieves the collection name for relays from the API settings configuration. /// /// The configured relays collection name as specified in the API settings. public override string GetCollectionName() { return _apiSettings.Relays; } /// /// Retrieves a from the collection by its identifier, returning the first match or null when no document is found or an error occurs while querying. /// /// The unique identifier of the relay to look up. /// The matching , or null if no document matches the identifier or the query fails. public async Task GetById(ObjectId relayId) { try { var filter = Builders.Filter.Eq(x => x.Id, relayId); var result = await Collection.FindAsync(filter, null); return result.FirstOrDefault(); } catch (Exception e) { Log.Error("Exception trying to get relay by id: {id}. Exception {e}", relayId, e); return null; } } /// /// Retrieves a list of relays from the collection whose identifiers are contained in the provided configuration list and whose type matches the specified value. /// /// The collection of relay identifiers used to filter relays by their Id field. /// The relay type used as an equality filter on the Type field. /// A list of instances matching both the identifier and type filters; an empty list is returned when no relays match. public List GetRelayByTypeInList(List configurationRelayList, RelayEnum.Type type) { var filterBuilder = Builders.Filter; var filter = filterBuilder.And( filterBuilder.In(r => r.Id, configurationRelayList), filterBuilder.Eq(r => r.Type, type) ); return Collection.Find(filter).ToList(); } /// /// Retrieves the entries whose identifiers are contained in the supplied list, returning all matches as a list. /// /// The collection of values used to match relays by their Id field. /// A containing the relays whose identifiers are found in ; an empty list is returned when no matching relays exist. public List GetRelayInList(List configurationRelayList) { var filterBuilder = Builders.Filter; var filter = filterBuilder.And( filterBuilder.In(r => r.Id, configurationRelayList)); return Collection.Find(filter).ToList(); } /// /// Retrieves a paginated, sortable set of relays, optionally filtered by a case-insensitive text match on the relay name. /// When the filter's text is null or empty, no text-based criteria are applied and the result is returned with the default ascending sort by relay name. /// /// The pagination criteria containing the optional text filter applied against the relay name. /// A fluent find query for entities, ordered by relay name in ascending order, ready for further pagination. public IFindFluent GetPaginatedRelays(PaginationFilter filter) { var filterBuilder = Builders.Filter; var sort = Builders.Sort.Ascending("relayName"); var filters = new List>(); if (filter.FilteredRequest == null) return CreateFindFluent(filters, sort); if (!string.IsNullOrEmpty(filter.FilteredRequest?.Text)) { var textFilter = filter.FilteredRequest.Text; var textFilterEscaped = Regex.Escape(textFilter); filters.Add( filterBuilder.Or( filterBuilder.Regex(p => p.RelayName, new BsonRegularExpression(textFilterEscaped, "i")) ) ); } return CreateFindFluent(filters, sort); } /// /// Asynchronously inserts a new relay into the collection and returns the persisted entity fetched by its identifier. /// On failure, the exception is logged and the method returns null instead of propagating the error. /// /// The relay entity to insert into the collection. /// The inserted relay retrieved by its identifier, or null if the insertion fails. public async Task InsertOneRelayAsync(Relay request) { try { await Collection.InsertOneAsync(request); return await GetById(request.Id); } catch (Exception ex) { Log.Error("Error inserting relay: {relay}. Exception: {ex}", JsonConvert.SerializeObject(request, Formatting.Indented), ex); return null; } } /// /// Updates an existing relay document identified by the specified object identifier with the provided relay data and returns the updated entity. /// /// The unique identifier of the relay document to update in the collection. /// The relay instance containing the new values to apply to the existing document. /// A task that represents the asynchronous operation, containing the updated after the update, or null if no matching document was found. public async Task UpdateRelayAsync(ObjectId objectId, Relay relay) { var filter = Builders.Filter.Eq("_id", objectId); var update = Builders.Update .Set(c => c.Mode, relay.Mode) .Set(c => c.RelayNumber, relay.RelayNumber) .Set(c => c.Username, relay.Username) .Set(c => c.Password, relay.Password) .Set(c => c.Driver, relay.Driver) .Set(c => c.Ip, relay.Ip) .Set(c => c.Port, relay.Port) .Set(c => c.RelayName, relay.RelayName); return await Collection.FindOneAndUpdateAsync(filter, update, new FindOneAndUpdateOptions { ReturnDocument = ReturnDocument.After }); } /// /// Retrieves a from the collection whose RelayName matches the specified name. /// Returns null when no relay with the given name exists in the collection. /// /// The name of the relay to look up. May be null, in which case the query matches relays with a null name. /// A containing the matching , or null if no relay with the specified name is found. public async Task GetByName(string? requestRelayName) { var filterBuilder = Builders.Filter; var filter = filterBuilder.Eq(r => r.RelayName, requestRelayName); return await Collection.Find(filter).FirstOrDefaultAsync(); } /// /// Creates a fluent find query for the Relay collection by combining the provided filters with a logical AND, or applying an empty filter when no filters are supplied, and then applying the given sort definition. /// /// The list of filter definitions to combine; when empty, an empty filter is used to match all documents. /// The sort definition applied to the query results. /// An representing the filtered and sorted Relay query. private IFindFluent CreateFindFluent(List> filters, SortDefinition sort) { var combinedFilter = filters.Any() ? Builders.Filter.And(filters) : Builders.Filter.Empty; return Collection.Find(combinedFilter).Sort(sort); } }