using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; public class MedicationService : IMedicationService { private readonly AppDbContext _db; private readonly MedicationCorrelationOptions _correlationOptions; public MedicationService( AppDbContext db, IOptions correlationOptions) { _db = db; _correlationOptions = correlationOptions.Value; } public async Task CreateAsync( Guid encounterId, CreateMedicationAdministrationRequest req) { var encounter = await _db.Encounters.FindAsync(encounterId); if (encounter is null) throw new NotFoundException("Encounter not found.", "ENCOUNTER_NOT_FOUND"); if (encounter.Status != EncounterStatus.Active) throw new ConflictException( "Cannot record medications for a non-active encounter.", "ENCOUNTER_NOT_ACTIVE"); var med = new MedicationAdministration { Id = Guid.NewGuid(), EncounterId = encounterId, DrugName = req.DrugName.Trim(), Dose = req.Dose, DoseUnit = req.DoseUnit.Trim(), Route = req.Route.Trim(), AdministeredAt = req.AdministeredAt ?? DateTimeOffset.UtcNow, AdministeredBy = req.AdministeredBy.Trim() }; _db.MedicationAdministrations.Add(med); await _db.SaveChangesAsync(); return med; } public async Task GetByIdAsync(Guid id) { var med = await _db.MedicationAdministrations .AsNoTracking() .Include(m => m.Encounter) .FirstOrDefaultAsync(m => m.Id == id); if (med is null) throw new NotFoundException("Medication administration not found.", "MEDICATION_NOT_FOUND"); return med; } public async Task> GetRecentForEncounterAsync( Guid encounterId, string observationCode, int windowMinutes) { var cutoff = DateTimeOffset.UtcNow.AddMinutes(-windowMinutes); var mappings = _correlationOptions.DrugVitalMappings; // Find drugs that affect this observation code var relevantDrugs = mappings .Where(kv => kv.Value.Contains(observationCode, StringComparer.OrdinalIgnoreCase)) .Select(kv => kv.Key) .ToHashSet(StringComparer.OrdinalIgnoreCase); if (relevantDrugs.Count == 0) return Array.Empty(); return await _db.MedicationAdministrations .AsNoTracking() .Where(m => m.EncounterId == encounterId && m.AdministeredAt >= cutoff && relevantDrugs.Contains(m.DrugName.ToLower())) .OrderByDescending(m => m.AdministeredAt) .ToListAsync(); } public async Task> ListByEncounterAsync( Guid encounterId, DateTimeOffset? since, int page, int pageSize) { var query = _db.MedicationAdministrations .AsNoTracking() .Where(m => m.EncounterId == encounterId); if (since.HasValue) query = query.Where(m => m.AdministeredAt >= since.Value); var total = await query.CountAsync(); var items = await query .OrderByDescending(m => m.AdministeredAt) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); return new PagedResult(items, page, pageSize, total); } }