class Dependabot::Uv::FileUpdater::RequirementReplacer

def add_space_after_commas?

def add_space_after_commas?
  original_dependency_declaration_string(old_requirement)
    .match(RequirementParser::REQUIREMENTS)
    .to_s.include?(", ")
end

def add_space_after_operators?

def add_space_after_operators?
  original_dependency_declaration_string(old_requirement)
    .match(RequirementParser::REQUIREMENTS)
    .to_s.match?(/#{RequirementParser::COMPARISON}\s+\d/o)
end

def hash_algorithm(requirement)

def hash_algorithm(requirement)
  return unless requirement_includes_hashes?(requirement)
  original_dependency_declaration_string(requirement)
    .match(RequirementParser::HASHES)
    .named_captures.fetch("algorithm")
end

def hash_separator(requirement)

def hash_separator(requirement)
  return unless requirement_includes_hashes?(requirement)
  hash_regex = RequirementParser::HASH
  current_separator =
    original_dependency_declaration_string(requirement)
    .match(/#{hash_regex}((?<separator>\s*\\?\s*?)#{hash_regex})*/)
    .named_captures.fetch("separator")
  default_separator =
    original_dependency_declaration_string(requirement)
    .match(RequirementParser::HASH)
    .pre_match.match(/(?<separator>\s*\\?\s*?)\z/)
    .named_captures.fetch("separator")
  current_separator || default_separator
end

def initialize(content:, dependency_name:, old_requirement:,

def initialize(content:, dependency_name:, old_requirement:,
               new_requirement:, new_hash_version: nil, index_urls: nil)
  @content          = content
  @dependency_name  = normalise(dependency_name)
  @old_requirement  = old_requirement
  @new_requirement  = new_requirement
  @new_hash_version = new_hash_version
  @index_urls = index_urls
end

def normalise(name)

def normalise(name)
  NameNormaliser.normalise(name)
end

def original_declaration_replacement_regex

def original_declaration_replacement_regex
  original_string =
    original_dependency_declaration_string(old_requirement)
  /(?<![\-\w\.\[])#{Regexp.escape(original_string)}(?![\-\w\.])/
end

def original_dependency_declaration_string(old_req)

def original_dependency_declaration_string(old_req)
  matches = []
  dec =
    if old_req.nil?
      regex = RequirementParser::INSTALL_REQ_WITHOUT_REQUIREMENT
      content.scan(regex) { matches << Regexp.last_match }
      matches.find { |m| normalise(m[:name]) == dependency_name }
    else
      regex = RequirementParser::INSTALL_REQ_WITH_REQUIREMENT
      content.scan(regex) { matches << Regexp.last_match }
      matches
        .select { |m| normalise(m[:name]) == dependency_name }
        .find { |m| requirements_match(m[:requirements], old_req) }
    end
  raise "Declaration not found for #{dependency_name}!" unless dec
  dec.to_s.strip
end

def package_hashes_for(name:, version:, algorithm:)

def package_hashes_for(name:, version:, algorithm:)
  index_urls = @index_urls || [nil]
  index_urls.map do |index_url|
    args = [name, version, algorithm]
    args << index_url unless index_url.nil?
    begin
      result = SharedHelpers.run_helper_subprocess(
        command: "pyenv exec python3 #{NativeHelpers.python_helper_path}",
        function: "get_dependency_hash",
        args: args
      )
    rescue SharedHelpers::HelperSubprocessFailed => e
      requirement_error_handler(e)
      raise unless e.message.include?("PackageNotFoundError")
      next
    end
    return result.map { |h| "--hash=#{algorithm}:#{h['hash']}" } if result.is_a?(Array)
  end
  raise Dependabot::DependencyFileNotResolvable, "Unable to find hashes for package #{name}"
end

def requirement_error_handler(error)

def requirement_error_handler(error)
  Dependabot.logger.warn(error.message)
  return unless error.message.match?(CERTIFICATE_VERIFY_FAILED)
  msg = "Error resolving dependency."
  raise DependencyFileNotResolvable, msg
end

def requirement_includes_hashes?(requirement)

def requirement_includes_hashes?(requirement)
  original_dependency_declaration_string(requirement)
    .match?(RequirementParser::HASHES)
end

def requirements_match(req1, req2)

def requirements_match(req1, req2)
  req1&.split(",")&.map { |r| r.gsub(/\s/, "") }&.sort ==
    req2&.split(",")&.map { |r| r.gsub(/\s/, "") }&.sort
end

def update_hashes?

def update_hashes?
  !new_hash_version.nil?
end

def updated_content

def updated_content
  updated_content =
    content.gsub(original_declaration_replacement_regex) do |mtch|
      # If the "declaration" is setting an option (e.g., no-binary)
      # ignore it, since it isn't actually a declaration
      next mtch if Regexp.last_match&.pre_match&.match?(/--.*\z/)
      updated_dependency_declaration_string
    end
  raise "Expected content to change!" if old_requirement != new_requirement && content == updated_content
  updated_content
end

def updated_dependency_declaration_string

def updated_dependency_declaration_string
  old_req = old_requirement
  updated_string =
    if old_req
      original_dependency_declaration_string(old_req)
        .sub(RequirementParser::REQUIREMENTS, updated_requirement_string)
    else
      original_dependency_declaration_string(old_req)
        .sub(RequirementParser::NAME_WITH_EXTRAS) do |nm|
          nm + updated_requirement_string
        end
    end
  return updated_string unless update_hashes? && requirement_includes_hashes?(old_req)
  updated_string.sub(
    RequirementParser::HASHES,
    package_hashes_for(
      name: dependency_name,
      version: new_hash_version,
      algorithm: hash_algorithm(old_req)
    ).join(hash_separator(old_req))
  )
end

def updated_requirement_string

def updated_requirement_string
  new_req_string = new_requirement
  new_req_string = new_req_string.gsub(/,\s*/, ", ") if add_space_after_commas?
  if add_space_after_operators?
    new_req_string =
      new_req_string
      .gsub(/(#{RequirementParser::COMPARISON})\s*(?=\d)/o, '\1 ')
  end
  new_req_string
end