using Hl7.Fhir.Model;
using FhirObservation = Hl7.Fhir.Model.Observation;

public static class ObservationMapper
{
    private static readonly Dictionary<string, string> LoincToVigilCare = new()
    {
        ["8867-4"] = "HEART_RATE",
        ["8310-5"] = "TEMP_C",
        ["8480-6"] = "BP_SYSTOLIC",
        ["8462-4"] = "BP_DIASTOLIC",
        ["9279-1"] = "RESP_RATE",
        ["2708-6"] = "SPO2",
        ["2345-7"] = "GLUCOSE_MG_DL",
        ["2823-3"] = "POTASSIUM_MEQ_L",
        ["2951-2"] = "SODIUM_MEQ_L",
        ["2524-7"] = "LACTATE_MMOL_L",
        ["6690-2"] = "WBC_K_UL",
        ["718-7"] = "HEMOGLOBIN_G_DL",
        ["2160-0"] = "CREATININE_MG_DL",
    };

    private static readonly HashSet<string> VitalSignCodes =
    [
        "HEART_RATE", "TEMP_C", "BP_SYSTOLIC", "BP_DIASTOLIC", "RESP_RATE", "SPO2"
    ];

    /// <summary>
    /// Maps a VigilCare Observation entity to a FHIR R4 Observation resource.
    ///
    /// Mapping decisions:
    /// - ObservationCode maps to LOINC codes where a standard mapping exists.
    ///   Unknown codes use a local CodeSystem with the original code as display.
    /// - Value + Unit maps to Observation.valueQuantity with UCUM unit codes.
    /// - RecordedAt maps to effectiveDateTime (when the observation was clinically
    ///   relevant), not issued (when the system recorded it).
    /// - Source and SourceBatchId are preserved as extensions for provenance.
    /// </summary>
    public static FhirObservation ToFhir(Observation entity, string baseUrl)
    {
        var observation = new FhirObservation
        {
            Id = entity.Id.ToString(),
            Meta = new Meta
            {
                VersionId = "1",
                LastUpdated = entity.CreatedAt,
            },
            Status = ObservationStatus.Final,
            Subject = new ResourceReference($"Patient/{entity.PatientId}"),
            Encounter = new ResourceReference($"Encounter/{entity.EncounterId}"),
            Effective = new FhirDateTime(entity.RecordedAt),
            Issued = entity.CreatedAt,
        };

        observation.Code = MapObservationCode(entity.ObservationCode);

        observation.Value = new Quantity
        {
            Value = entity.Value,
            Unit = entity.Unit,
            System = "http://unitsofmeasure.org",
            Code = MapToUcum(entity.Unit),
        };

        var category = IsVitalSign(entity.ObservationCode) ? "vital-signs" : "laboratory";
        observation.Category.Add(new CodeableConcept(
            "http://terminology.hl7.org/CodeSystem/observation-category",
            category));

        if (!string.IsNullOrEmpty(entity.Note))
        {
            observation.Note.Add(new Annotation { Text = new Markdown(entity.Note) });
        }

        observation.Extension.Add(new Extension(
            "urn:vigilcare:source",
            new FhirString(entity.Source)));

        if (entity.SourceBatchId.HasValue)
        {
            observation.Extension.Add(new Extension(
                "urn:vigilcare:source-batch-id",
                new FhirString(entity.SourceBatchId.Value.ToString())));
        }

        return observation;
    }

    /// <summary>
    /// Reverse LOINC → VigilCare code mapping for FHIR search by LOINC code.
    /// </summary>
    public static IReadOnlyDictionary<string, string> GetReverseLoincMapping() => LoincToVigilCare;

    /// <summary>
    /// VigilCare observation codes that represent vital signs (used for category search).
    /// </summary>
    public static IReadOnlyCollection<string> GetVitalSignCodes() => VitalSignCodes;

    private static CodeableConcept MapObservationCode(string code) => code switch
    {
        "HEART_RATE"       => Loinc("8867-4", "Heart rate"),
        "TEMP_C"           => Loinc("8310-5", "Body temperature"),
        "BP_SYSTOLIC"      => Loinc("8480-6", "Systolic blood pressure"),
        "BP_DIASTOLIC"     => Loinc("8462-4", "Diastolic blood pressure"),
        "RESP_RATE"        => Loinc("9279-1", "Respiratory rate"),
        "SPO2"             => Loinc("2708-6", "Oxygen saturation"),
        "GLUCOSE_MG_DL"    => Loinc("2345-7", "Glucose [Mass/volume] in Serum or Plasma"),
        "POTASSIUM_MEQ_L"  => Loinc("2823-3", "Potassium [Moles/volume] in Serum or Plasma"),
        "SODIUM_MEQ_L"     => Loinc("2951-2", "Sodium [Moles/volume] in Serum or Plasma"),
        "LACTATE_MMOL_L"   => Loinc("2524-7", "Lactate [Moles/volume] in Serum or Plasma"),
        "WBC_K_UL"         => Loinc("6690-2", "Leukocytes [#/volume] in Blood"),
        "HEMOGLOBIN_G_DL"  => Loinc("718-7", "Hemoglobin [Mass/volume] in Blood"),
        "CREATININE_MG_DL" => Loinc("2160-0", "Creatinine [Mass/volume] in Serum or Plasma"),
        _ => new CodeableConcept("urn:vigilcare:observation-code", code, code),
    };

    private static CodeableConcept Loinc(string code, string display) =>
        new("http://loinc.org", code, display);

    private static string MapToUcum(string unit) => unit switch
    {
        "bpm"          => "/min",
        "C"            => "Cel",
        "mmHg"         => "mm[Hg]",
        "breaths/min"  => "/min",
        "%"            => "%",
        "mg/dL"        => "mg/dL",
        "mEq/L"        => "meq/L",
        "mmol/L"       => "mmol/L",
        "K/uL"         => "10*3/uL",
        "g/dL"         => "g/dL",
        _ => unit,
    };

    private static bool IsVitalSign(string code) => VitalSignCodes.Contains(code);
}
