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<IReadOnlyList<MappedObservation>> 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<MappedObservation>();

        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<MappedObservation> 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)];
    }
}
