def handle_itc_response(raw, flaky_api_call: false)
      return unless raw
      return unless raw.kind_of?(Hash)

      data = raw['data'] || raw # sometimes it's with data, sometimes it isn't

      error_keys = ["sectionErrorKeys", "validationErrors", "serviceErrors"]
      info_keys = ["sectionInfoKeys", "sectionWarningKeys"]
      error_and_info_keys_to_check = error_keys + info_keys

      errors_in_data = fetch_errors_in_data(data_section: data, keys: error_and_info_keys_to_check)
      errors_in_version_info = fetch_errors_in_data(data_section: data, sub_section_name: "versionInfo", keys: error_and_info_keys_to_check)

      # If we have any errors or "info" we need to treat them as warnings or errors
      if errors_in_data.count == 0 && errors_in_version_info.count == 0
        logger.debug("Request was successful")
      end

      # We pass on the `current_language` so that the error message tells the user
      # what language the error was caused in
      handle_response_hash = lambda do |hash, current_language = nil|
        errors = []
        if hash.kind_of?(Hash)
          current_language ||= hash["language"]

          hash.each do |key, value|
            errors += handle_response_hash.call(value, current_language)

            next unless key == 'errorKeys' && value.kind_of?(Array) && value.count > 0
            # Prepend the error with the language so it's easier to understand for the user
            errors += value.collect do |current_error_message|
              current_language ? "[#{current_language}]: #{current_error_message}" : current_error_message
            end
          end
        elsif hash.kind_of?(Array)
          hash.each do |value|
            errors += handle_response_hash.call(value)
          end
          # else: We don't care about simple values
        end
        return errors
      end

      errors = handle_response_hash.call(data)

      # Search at data level, as well as "versionInfo" level for errors
      errors_in_data = fetch_errors_in_data(data_section: data, keys: error_keys)
      errors_in_version_info = fetch_errors_in_data(data_section: data, sub_section_name: "versionInfo", keys: error_keys)

      errors += errors_in_data.values if errors_in_data.values
      errors += errors_in_version_info.values if errors_in_version_info.values
      errors = errors.flat_map { |value| value }

      # Sometimes there is a different kind of error in the JSON response
      # e.g. {"warn"=>nil, "error"=>["operation_failed"], "info"=>nil}
      different_error = raw.fetch('messages', {}).fetch('error', nil)
      errors << different_error if different_error

      if errors.count > 0 # they are separated by `.` by default
        # Sample `error` content: [["Forbidden"]]
        if errors.count == 1 && errors.first == "You haven't made any changes."
          # This is a special error which we really don't care about
        elsif errors.count == 1 && errors.first.include?("try again later")
          raise ITunesConnectTemporaryError.new, errors.first
        elsif errors.count == 1 && errors.first.include?("Forbidden")
          raise_insufficient_permission_error!
        elsif flaky_api_call
          raise ITunesConnectPotentialServerError.new, errors.join(' ')
        else
          raise ITunesConnectError.new, errors.join(' ')
        end
      end

      # Search at data level, as well as "versionInfo" level for info and warnings
      info_in_data = fetch_errors_in_data(data_section: data, keys: info_keys)
      info_in_version_info = fetch_errors_in_data(data_section: data, sub_section_name: "versionInfo", keys: info_keys)

      info_in_data.each do |info_key, info_value|
        puts(info_value)
      end

      info_in_version_info.each do |info_key, info_value|
        puts(info_value)
      end

      return data
    end
def handle_itc_response(raw, flaky_api_call: false)
      return unless raw
      return unless raw.kind_of?(Hash)

      data = raw['data'] || raw # sometimes it's with data, sometimes it isn't

      error_keys = ["sectionErrorKeys", "validationErrors", "serviceErrors"]
      info_keys = ["sectionInfoKeys", "sectionWarningKeys"]
      error_and_info_keys_to_check = error_keys + info_keys

      errors_in_data = fetch_errors_in_data(data_section: data, keys: error_and_info_keys_to_check)
      errors_in_version_info = fetch_errors_in_data(data_section: data, sub_section_name: "versionInfo", keys: error_and_info_keys_to_check)

      # If we have any errors or "info" we need to treat them as warnings or errors
      if errors_in_data.count == 0 && errors_in_version_info.count == 0
        logger.debug("Request was successful")
      end

      # We pass on the `current_language` so that the error message tells the user
      # what language the error was caused in
      handle_response_hash = lambda do |hash, current_language = nil|
        errors = []
        if hash.kind_of?(Hash)
          current_language ||= hash["language"]

          hash.each do |key, value|
            errors += handle_response_hash.call(value, current_language)

            next unless key == 'errorKeys' && value.kind_of?(Array) && value.count > 0
            # Prepend the error with the language so it's easier to understand for the user
            errors += value.collect do |current_error_message|
              current_language ? "[#{current_language}]: #{current_error_message}" : current_error_message
            end
          end
        elsif hash.kind_of?(Array)
          hash.each do |value|
            errors += handle_response_hash.call(value)
          end
          # else: We don't care about simple values
        end
        return errors
      end

      errors = handle_response_hash.call(data)

      # Search at data level, as well as "versionInfo" level for errors
      errors_in_data = fetch_errors_in_data(data_section: data, keys: error_keys)
      errors_in_version_info = fetch_errors_in_data(data_section: data, sub_section_name: "versionInfo", keys: error_keys)

      errors += errors_in_data.values if errors_in_data.values
      errors += errors_in_version_info.values if errors_in_version_info.values
      errors = errors.flat_map { |value| value }

      # Sometimes there is a different kind of error in the JSON response
      # e.g. {"warn"=>nil, "error"=>["operation_failed"], "info"=>nil}
      different_error = raw.fetch('messages', {}).fetch('error', nil)
      errors << different_error if different_error

      if errors.count > 0 # they are separated by `.` by default
        # Sample `error` content: [["Forbidden"]]
        if errors.count == 1 && errors.first == "You haven't made any changes."
          # This is a special error which we really don't care about
        elsif errors.count == 1 && errors.first.include?("try again later")
          raise ITunesConnectTemporaryError.new, errors.first
        elsif errors.count == 1 && errors.first.include?("Forbidden")
          raise_insufficient_permission_error!
        elsif flaky_api_call
          raise ITunesConnectPotentialServerError.new, errors.join(' ')
        else
          raise ITunesConnectError.new, errors.join(' ')
        end
      end

      # Search at data level, as well as "versionInfo" level for info and warnings
      info_in_data = fetch_errors_in_data(data_section: data, keys: info_keys)
      info_in_version_info = fetch_errors_in_data(data_section: data, sub_section_name: "versionInfo", keys: info_keys)

      info_in_data.each do |info_key, info_value|
        puts(info_value)
      end

      info_in_version_info.each do |info_key, info_value|
        puts(info_value)
      end

      return data
    end
rubocop:disable Metrics/PerceivedComplexity
 If the response is coming from a flaky api, set flaky_api_call to true so we retry a little.
 Patience is a virtue.