You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: error_generator.rb
    path of file: ./repos/identity-idp/app/services/doc_auth
    
    the code of the file until where you have to start completion: # frozen_string_literal: true

module DocAuth
  class ErrorGenerator
    include SelfieConcern
    attr_reader :config

    def initialize(config)
      @config = config
    end

    # These constants are the key names for the TrueID errors hash that is returned
    ID = :id
    FR
    
    def generate_doc_auth_errors(response_info)
      alert_error_count = response_info[:doc_auth_result] == 'Passed' ?
        0 : response_info[:alert_failure_count]

      unknown_fail_count = scan_for_unknown_alerts(response_info)
      alert_error_count -= unknown_fail_count

      # If we have document type errors (Ex: passport was uploaded) return only
      # document type errors for both the "FRONT" and "BACK" fields (but not "SELFIE")
      # this return will never include any selfie errors at the moment.
      doc_type_errors = get_id_type_errors(response_info[:classification_info])
      return doc_type_errors.to_h unless doc_type_errors.nil? || doc_type_errors.empty?

      # If we have image metric errors (Ex: DPI too low) return only
      # image metric errors for both the "FRONT" and "BACK" fields (but not "SELFIE")
      # this return will never include any selfie errors at the moment.
      image_metric_errors = get_image_metric_errors(response_info[:image_metrics])
      return image_metric_errors.to_h unless image_metric_errors.empty?

      # If we have a selfie failure (selfie does not match id), then all three fields:
      # front, back, and selfie should fail with specific in line errors.
      liveness_enabled = response_info[:liveness_enabled]
      selfie_error = get_selfie_error(liveness_enabled, response_info)
      if selfie_error == Errors::SELFIE_FAILURE
        # This returns the same sort of object that ErrorResult.to_h returns
        # but we need to do something more complex than that's set up to handle
        return {
          general: [Errors::SELFIE_NOT_LIVE],
          front: [Errors::MULTIPLE_FRONT_ID_FAILURES],
          back: [Errors::MULTIPLE_BACK_ID_FAILURES],
          selfie: [Errors::SELFIE_FAILURE],
          hints: false,
        }
      end

      # This block can also return errors for front, back, and/or selfie
      # But we only get here if none of the returns above are triggered
      alert_errors = get_error_messages(liveness_enabled, response_info)
      alert_error_count += 1 if alert_errors.include?(SELFIE)

      error = ''
      side = nil

      # If we don't have document type or image metric errors then sort out which
      # errors to return. Note that there's a :general error added in the
      # `to_h` method of error_result
      if alert_error_count < 1
        config.warn_notifier&.call(
          message: 'DocAuth failure escaped without useful errors',
          response_info: response_info,
        )

        error = Errors::GENERAL_ERROR
        side = ID
      elsif alert_error_count == 1
        error = alert_errors.values[0].to_a.pop
        side = alert_errors.keys[0]
      elsif alert_error_count > 1
        # Simplify multiple errors into a single error for the user
        error_fields = alert_errors.keys
        if error_fields.length == 1
          side = error_fields.first
          case side
          when ID
            error = Errors::GENERAL_ERROR
          when FRONT
            error = Errors::MULTIPLE_FRONT_ID_FAILURES
          when BACK
            error = Errors::MULTIPLE_BACK_ID_FAILURES
          end
        elsif error_fields.length > 1
          error = Errors::GENERAL_ERROR
          side = ID
        end
      end

      ErrorResult.new(error, side).to_h
    end

    # private

    def get_id_type_errors(classification_info)
      return unless classification_info.present?
      error_result = ErrorResult.new
      both_side_ok = true
      %w[Front Back].each do |side|
        side_class = classification_info.with_indifferent_access.dig(side, 'ClassName')
        side_country = classification_info.with_indifferent_access.dig(side, 'CountryCode')
        side_issuer_type = classification_info.with_indifferent_access.dig(side, 'IssuerType')

        side_ok = !side_class.present? ||
                  SUPPORTED_ID_CLASSNAME.include?(side_class) ||
                  side_class == 'Unknown'
        country_ok = !side_country.present? || supported_country_codes.include?(side_country)
        issuer_type_ok = !side_issuer_type.present? ||
                         ACCEPTED_ISSUER_TYPES.include?(side_issuer_type)
        both_side_ok &&= issuer_type_ok && side_ok && country_ok
        error_result.add_side(side.downcase.to_sym) unless side_ok && issuer_type_ok && country_ok
      end
      unless both_side_ok
        error_result.set_error(Errors::DOC_TYPE_CHECK)
      end
      error_result
    end

    def get_image_metric_errors(processed_image_metrics)
      dpi_threshold = config&.dpi_threshold&.to_i || 290
      sharpness_threshold = config&.sharpness_threshold&.to_i || 40
      glare_threshold = config&.glare_threshold&.to_i || 40

      dpi_metrics, sharp_metrics, glare_metrics = {}, {}, {}
      error_result = ErrorResult.new

      processed_image_metrics.each do |side, img_metrics|
        dpi_metrics[side] = img_metrics.slice('HorizontalResolution', 'VerticalResolution')
        sharp_metrics[side] = img_metrics.slice('SharpnessMetric')
        glare_metrics[side] = img_metrics.slice('GlareMetric')
      end

      dpi_metrics.each do |side, img_metrics|
        hdpi = img_metrics['HorizontalResolution']&.to_i || 0
        vdpi = img_metrics['VerticalResolution']&.to_i || 0
        if hdpi < dpi_threshold || vdpi < dpi_threshold
          error_result.set_error(Errors::DPI_LOW)
          error_result.add_side(side)
        end
      end
      return error_result unless error_result.empty?

      sharp_metrics.each do |side, img_metrics|
        sharpness = img_metrics['SharpnessMetric']&.to_i
        if sharpness.present? && sharpness < sharpness_threshold
          error_result.set_error(Errors::SHARP_LOW)
          error_result.add_side(side)
        end
      end