using Hl7.Fhir.Model;
using Microsoft.Extensions.Options;
using Task = System.Threading.Tasks.Task;

public class FhirReferenceResolver
{
    private readonly IExternalIdentifierService _identifiers;
    private readonly FhirOptions _options;

    public FhirReferenceResolver(
        IExternalIdentifierService identifiers,
        IOptions<FhirOptions> options)
    {
        _identifiers = identifiers;
        _options = options.Value;
    }

    public async Task<Guid> ResolvePatientReferenceAsync(ResourceReference reference)
    {
        if (reference.Identifier is not null)
            return await ResolveByIdentifierAsync(
                ExternalResourceType.Patient,
                reference.Identifier.System,
                reference.Identifier.Value,
                _options.PatientIdentifierSystems);

        if (FhirMappingHelpers.TryParseResourceReference(reference.Reference, out var type, out var id)
            && type == "Patient"
            && Guid.TryParse(id, out var guid))
        {
            return guid;
        }

        throw new FhirMappingException(
            "Patient reference must include a resolvable identifier or UUID.",
            "required");
    }

    public async Task<Guid> ResolveEncounterReferenceAsync(ResourceReference reference)
    {
        if (reference.Identifier is not null)
            return await ResolveByIdentifierAsync(
                ExternalResourceType.Encounter,
                reference.Identifier.System,
                reference.Identifier.Value,
                _options.EncounterIdentifierSystems);

        if (FhirMappingHelpers.TryParseResourceReference(reference.Reference, out var type, out var id)
            && type == "Encounter"
            && Guid.TryParse(id, out var guid))
        {
            return guid;
        }

        throw new FhirMappingException(
            "Encounter reference must include a resolvable identifier or UUID.",
            "required");
    }

    public (string System, string Value)? ExtractPrimaryIdentifier(
        IIdentifiable<List<Identifier>> resource, string[] acceptedSystems)
    {
        foreach (var system in acceptedSystems)
        {
            var match = resource.Identifier?
                .FirstOrDefault(i => i.System == system && !string.IsNullOrWhiteSpace(i.Value));
            if (match is not null)
                return (match.System!, match.Value!);
        }

        return resource.Identifier?
            .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i.System) && !string.IsNullOrWhiteSpace(i.Value))
            is { } fallback
            ? (fallback.System!, fallback.Value!)
            : null;
    }

    private async Task<Guid> ResolveByIdentifierAsync(
        ExternalResourceType type, string? system, string? value, string[] acceptedSystems)
    {
        if (string.IsNullOrWhiteSpace(system) || string.IsNullOrWhiteSpace(value))
            throw new FhirMappingException("Identifier system and value are required.", "required");

        if (!acceptedSystems.Contains(system))
            throw new FhirMappingException(
                $"Identifier system '{system}' is not configured.", "not-supported");

        var internalId = await _identifiers.ResolveInternalIdAsync(type, system, value);
        if (internalId is null)
            throw new NotFoundException(
                $"{type} with identifier {system}|{value} not found.",
                "FHIR_RESOURCE_NOT_FOUND");

        return internalId.Value;
    }
}
