using Hl7.Fhir.Model; using Task = System.Threading.Tasks.Task; public record MappedObservation(IngestObservationRequest Request, Guid EncounterId); public class ObservationFhirMapper { private readonly FhirReferenceResolver _refs; public ObservationFhirMapper(FhirReferenceResolver refs) => _refs = refs; public async Task> ToIngestRequestsAsync(Hl7.Fhir.Model.Observation fhir) { if (fhir.Encounter is null) throw new FhirMappingException("Observation encounter reference is required.", "required"); var encounterId = await _refs.ResolveEncounterReferenceAsync(fhir.Encounter); var source = MapSource(fhir); var recordedAt = fhir.Effective.ToUtcDateTimeOffset() ?? DateTimeOffset.UtcNow; var idempotencyKey = fhir.Identifier?.FirstOrDefault()?.Value ?? fhir.Id; var results = new List(); if (fhir.Component?.Count > 0) { foreach (var component in fhir.Component) results.AddRange(MapSingleCoding( component.Code, component.Value, encounterId, source, recordedAt, idempotencyKey is null ? null : $"{idempotencyKey}:{component.Code?.Coding?.FirstOrDefault()?.Code}")); } else { results.AddRange(MapSingleCoding( fhir.Code, fhir.Value, encounterId, source, recordedAt, idempotencyKey)); } if (results.Count == 0) throw new FhirMappingException("Observation contains no mappable values.", "invalid"); return results; } private static ObservationSource MapSource(Hl7.Fhir.Model.Observation fhir) { var category = fhir.Category?.FirstOrDefault()?.Coding?.FirstOrDefault()?.Code; return category switch { "vital-signs" => ObservationSource.Device, "laboratory" => ObservationSource.Lab, _ => ObservationSource.Manual }; } private static List MapSingleCoding( CodeableConcept? code, DataType? value, Guid encounterId, ObservationSource source, DateTimeOffset recordedAt, string? idempotencyKey) { var coding = code?.Coding?.FirstOrDefault(c => !string.IsNullOrWhiteSpace(c.System) && !string.IsNullOrWhiteSpace(c.Code)); if (coding is null) throw new FhirMappingException("Observation code coding is required.", "required"); if (!LoincCodeMapper.TryMap(coding.System!, coding.Code!, out var mapping)) throw new FhirMappingException( $"Unsupported observation code {coding.System}|{coding.Code}.", "not-supported"); if (value is not Quantity qty) throw new FhirMappingException("Observation valueQuantity is required.", "required"); var (normalizedValue, _) = FhirUnitConverter.Normalize( mapping.InternalCode, qty.Value ?? 0m, qty.Unit, mapping.AllowFahrenheit); var request = new IngestObservationRequest( ObservationCode: mapping.InternalCode, Value: normalizedValue, Unit: mapping.ExpectedUnit, Source: source, RecordedAt: recordedAt, IdempotencyKey: idempotencyKey); return [new MappedObservation(request, encounterId)]; } }