"""
Generate the low-level `src/AWSServices.jl` file with all definitions.
"""
function _generate_low_level_wrappers(
    service_files::AbstractArray{ServiceFile}, auth::GitHub.Authorization
)
    service_definitions = sort!(_generate_low_level_definitions(service_files, auth))

    template = """
    # This file is auto-generated by AWSMetadata.jl

    module AWSServices

    using AWS
    using OrderedCollections: LittleDict

    $(join(service_definitions, "\n"))

    end
    """

    open(services_path, "w") do f
        print(f, template)
    end

    return template
end

"""
Get the low-level definitions for all AWS Services.
"""
function _generate_low_level_definitions(
    service_files::AbstractArray{ServiceFile}, auth::GitHub.Authorization
)
    low_level_defs = Vector{String}(undef, length(service_files))

    Threads.@threads for i in eachindex(service_files)
        service_file = service_files[i]
        service_name = service_file.name
        @info "Generating low level wrapper for $service_name"

        service = service_definition(service_file; auth=auth)
        service_metadata = service["metadata"]

        low_level_defs[i] = _generate_low_level_definition(service_metadata)
    end

    return low_level_defs
end

"""
Get the low-level definition for an AWS Service.
"""
function _generate_low_level_definition(service::AbstractDict)
    protocol = service["protocol"]
    endpoint_prefix = service["endpointPrefix"]
    signing_name =
        haskey(service, "signingName") ? service["signingName"] : service["endpointPrefix"]
    service_id = replace(lowercase(service["serviceId"]), ' ' => '_')
    api_version = service["apiVersion"]

    service_specifics = LittleDict{String,String}()

    if service_id == "glacier"
        service_specifics[service_id] = "LittleDict(\"x-amz-glacier-version\" => \"$(service["apiVersion"])\")"
    end

    if protocol == "rest-xml"
        return "const $service_id = AWS.RestXMLService(\"$signing_name\", \"$endpoint_prefix\", \"$api_version\")"
    elseif protocol in ("ec2", "query")
        return "const $service_id = AWS.QueryService(\"$signing_name\", \"$endpoint_prefix\", \"$api_version\")"
    elseif protocol == "rest-json" && haskey(service_specifics, service_id)
        return "const $service_id = AWS.RestJSONService(\"$signing_name\", \"$endpoint_prefix\", \"$api_version\", $(service_specifics[service_id]))"
    elseif protocol == "rest-json"
        return "const $service_id = AWS.RestJSONService(\"$signing_name\", \"$endpoint_prefix\", \"$api_version\")"
    elseif protocol == "json"
        json_version = service["jsonVersion"]
        target = service["targetPrefix"]
        return "const $service_id = AWS.JSONService(\"$signing_name\", \"$endpoint_prefix\", \"$api_version\", \"$json_version\", \"$target\")"
    else
        throw(
            ProtocolNotDefined(
                "$service_id is using a new protocol; $protocol which is not supported."
            ),
        )
    end
end
