using Hl7.Fhir.Model;
using Microsoft.Extensions.Options;

public class PatientFhirMapper
{
    private readonly FhirReferenceResolver _refs;
    private readonly FhirOptions _options;

    public PatientFhirMapper(FhirReferenceResolver refs, IOptions<FhirOptions> options)
    {
        _refs = refs;
        _options = options.Value;
    }

    public FhirPatientUpsertRequest ToUpsertRequest(Hl7.Fhir.Model.Patient fhir)
    {
        var identifier = _refs.ExtractPrimaryIdentifier(fhir, _options.PatientIdentifierSystems)
            ?? throw new FhirMappingException("Patient must include at least one identifier.", "required");

        var name = fhir.Name?.FirstOrDefault()
            ?? throw new FhirMappingException("Patient must include a name.", "required");

        var given = name.Given?.FirstOrDefault() ?? "";
        var family = name.Family ?? "";

        if (fhir.BirthDate is null)
            throw new FhirMappingException("Patient birthDate is required.", "required");

        return new FhirPatientUpsertRequest(
            IdentifierSystem: identifier.System,
            IdentifierValue: identifier.Value,
            FirstName: given,
            LastName: family,
            DateOfBirth: DateOnly.Parse(fhir.BirthDate),
            Gender: fhir.Gender?.ToString()?.ToLowerInvariant() ?? "unknown");
    }

    public Hl7.Fhir.Model.Patient ToFhirResponse(Patient patient, (string System, string Value)? hospitalId)
    {
        var resource = new Hl7.Fhir.Model.Patient
        {
            Id = patient.Id.ToString(),
            Identifier = new List<Identifier>()
        };

        if (hospitalId is not null)
        {
            resource.Identifier.Add(new Identifier
            {
                System = hospitalId.Value.System,
                Value = hospitalId.Value.Value,
                Type = new CodeableConcept("http://terminology.hl7.org/CodeSystem/v2-0203", "MR")
            });
        }

        resource.Identifier.Add(new Identifier
        {
            System = _options.InternalPatientIdSystem,
            Value = patient.Id.ToString()
        });

        resource.Name.Add(new HumanName
        {
            Given = new[] { patient.FirstName },
            Family = patient.LastName
        });
        resource.BirthDate = patient.DateOfBirth.ToString("yyyy-MM-dd");
        resource.Gender = patient.Gender switch
        {
            "male" => AdministrativeGender.Male,
            "female" => AdministrativeGender.Female,
            _ => AdministrativeGender.Unknown
        };

        return resource;
    }
}