class Dependabot::UpdateCheckers::Base

def active_advisories

def active_advisories
  security_advisories.select { |a| a.vulnerable?(T.must(current_version)) }
end

def can_compare_requirements?

def can_compare_requirements?
  (version_from_requirements &&
    latest_version &&
    version_class.correct?(latest_version.to_s)) || false
end

def can_update?(requirements_to_unlock:)

def can_update?(requirements_to_unlock:)
  # Can't update if all versions are being ignored
  return false if ignore_requirements.include?(requirement_class.new(">= 0"))
  if dependency.version
    version_can_update?(requirements_to_unlock: requirements_to_unlock)
  else
    # TODO: Handle full unlock updates for dependencies without a lockfile
    return false if requirements_to_unlock == :none
    requirements_can_update?
  end
end

def changed_requirements

def changed_requirements
  (updated_requirements - dependency.requirements)
end

def conflicting_dependencies

def conflicting_dependencies
  [] # return an empty array for ecosystems that don't support this yet
end

def current_version

def current_version
  @current_version ||=
    T.let(
      dependency.numeric_version,
      T.nilable(Dependabot::Version)
    )
end

def existing_version_is_sha?

def existing_version_is_sha?
  return false if version_class.correct?(dependency.version)
  T.must(dependency.version).match?(/^[0-9a-f]{6,}$/)
end

def ignore_requirements

def ignore_requirements
  ignored_versions.flat_map { |req| requirement_class.requirements_array(req) }
end

def initialize(

def initialize(
  dependency:,
  dependency_files:,
  credentials:,
  repo_contents_path: nil,
  ignored_versions: [],
  raise_on_ignored: false,
  security_advisories: [],
  requirements_update_strategy: nil,
  dependency_group: nil,
  update_cooldown: nil,
  options: {}
)
  @dependency = dependency
  @dependency_files = dependency_files
  @repo_contents_path = repo_contents_path
  @credentials = credentials
  @requirements_update_strategy = requirements_update_strategy
  @ignored_versions = ignored_versions
  @raise_on_ignored = raise_on_ignored
  @security_advisories = security_advisories
  @dependency_group = dependency_group
  @update_cooldown = update_cooldown
  @options = options
end

def latest_resolvable_previous_version(_updated_version)

def latest_resolvable_previous_version(_updated_version)
  dependency.version
end

def latest_resolvable_version

def latest_resolvable_version
  raise NotImplementedError, "#{self.class} must implement #latest_resolvable_version"
end

def latest_resolvable_version_with_no_unlock

def latest_resolvable_version_with_no_unlock
  raise NotImplementedError, "#{self.class} must implement #latest_resolvable_version_with_no_unlock"
end

def latest_version

def latest_version
  raise NotImplementedError, "#{self.class} must implement #latest_version"
end

def latest_version_resolvable_with_full_unlock?

def latest_version_resolvable_with_full_unlock?
  raise NotImplementedError, "#{self.class} must implement #latest_version_resolvable_with_full_unlock?"
end

def lowest_resolvable_security_fix_version

def lowest_resolvable_security_fix_version
  raise NotImplementedError, "#{self.class} must implement #lowest_resolvable_security_fix_version"
end

def lowest_security_fix_version

def lowest_security_fix_version
  raise NotImplementedError, "#{self.class} must implement #lowest_security_fix_version"
end

def numeric_version_can_update?(requirements_to_unlock:)

def numeric_version_can_update?(requirements_to_unlock:)
  return false if numeric_version_up_to_date?
  case requirements_to_unlock&.to_sym
  when :none
    new_version = latest_resolvable_version_with_no_unlock
    return false unless new_version
    new_version > current_version
  when :own
    preferred_version_resolvable_with_unlock?
  when :all
    latest_version_resolvable_with_full_unlock?
  else raise "Unknown unlock level '#{requirements_to_unlock}'"
  end
end

def numeric_version_up_to_date?

def numeric_version_up_to_date?
  return false unless latest_version
  # If a lockfile isn't out of date and the package has switched to a git
  # source then we'll get a numeric version switching to a git SHA. In
  # this case we treat the version as up-to-date so that it's ignored.
  return true if latest_version.to_s.match?(/^[0-9a-f]{40}$/)
  T.must(latest_version) <= current_version
end

def preferred_resolvable_version

def preferred_resolvable_version
  # If this dependency is vulnerable, prefer trying to update to the
  # lowest_resolvable_security_fix_version. Otherwise update all the way
  # to the latest_resolvable_version.
  return lowest_resolvable_security_fix_version if vulnerable?
  latest_resolvable_version
rescue NotImplementedError
  latest_resolvable_version
end

def preferred_version_resolvable_with_unlock?

def preferred_version_resolvable_with_unlock?
  new_version = preferred_resolvable_version
  return false unless new_version
  if existing_version_is_sha?
    return false if new_version.to_s.start_with?(T.must(dependency.version))
  elsif new_version <= current_version
    return false
  end
  updated_requirements.none? { |r| r[:requirement] == :unfixable }
end

def requirement_class

def requirement_class
  dependency.requirement_class
end

def requirements_can_update?

def requirements_can_update?
  return false if changed_requirements.none?
  changed_requirements.none? { |r| r[:requirement] == :unfixable }
end

def requirements_unlocked_or_can_be?

def requirements_unlocked_or_can_be?
  true
end

def requirements_up_to_date?

def requirements_up_to_date?
  return T.must(version_from_requirements) >= version_class.new(latest_version.to_s) if can_compare_requirements?
  changed_requirements.none?
end

def sha1_version_can_update?(requirements_to_unlock:)

def sha1_version_can_update?(requirements_to_unlock:)
  return false if sha1_version_up_to_date?
  # All we can do with SHA-1 hashes is check for presence and equality
  case requirements_to_unlock&.to_sym
  when :none
    new_version = latest_resolvable_version_with_no_unlock
    !new_version&.to_s&.start_with?(T.must(dependency.version))
  when :own
    preferred_version_resolvable_with_unlock?
  when :all
    latest_version_resolvable_with_full_unlock?
  else raise "Unknown unlock level '#{requirements_to_unlock}'"
  end
end

def sha1_version_up_to_date?

def sha1_version_up_to_date?
  latest_version&.to_s&.start_with?(T.must(dependency.version)) || false
end

def up_to_date?

def up_to_date?
  if dependency.version
    version_up_to_date?
  else
    requirements_up_to_date?
  end
end

def updated_dependencies(requirements_to_unlock:)

def updated_dependencies(requirements_to_unlock:)
  return [] unless can_update?(requirements_to_unlock: requirements_to_unlock)
  case requirements_to_unlock&.to_sym
  when :none then [updated_dependency_without_unlock]
  when :own then [updated_dependency_with_own_req_unlock]
  when :all then updated_dependencies_after_full_unlock
  else raise "Unknown unlock level '#{requirements_to_unlock}'"
  end
end

def updated_dependencies_after_full_unlock

def updated_dependencies_after_full_unlock
  raise NotImplementedError
end

def updated_dependency_with_own_req_unlock

def updated_dependency_with_own_req_unlock
  version = preferred_resolvable_version.to_s
  previous_version = latest_resolvable_previous_version(version)
  Dependency.new(
    name: dependency.name,
    version: version,
    requirements: updated_requirements,
    previous_version: previous_version,
    previous_requirements: dependency.requirements,
    package_manager: dependency.package_manager,
    metadata: dependency.metadata,
    subdependency_metadata: dependency.subdependency_metadata
  )
end

def updated_dependency_without_unlock

def updated_dependency_without_unlock
  version = latest_resolvable_version_with_no_unlock.to_s
  previous_version = latest_resolvable_previous_version(version)
  Dependency.new(
    name: dependency.name,
    version: version,
    requirements: dependency.requirements,
    previous_version: previous_version,
    previous_requirements: dependency.requirements,
    package_manager: dependency.package_manager,
    metadata: dependency.metadata,
    subdependency_metadata: dependency.subdependency_metadata
  )
end

def updated_requirements

def updated_requirements
  raise NotImplementedError
end

def version_can_update?(requirements_to_unlock:)

def version_can_update?(requirements_to_unlock:)
  if existing_version_is_sha?
    return sha1_version_can_update?(
      requirements_to_unlock: requirements_to_unlock
    )
  end
  numeric_version_can_update?(
    requirements_to_unlock: requirements_to_unlock
  )
end

def version_class

def version_class
  dependency.version_class
end

def version_from_requirements

def version_from_requirements
  @version_from_requirements ||=
    T.let(
      dependency.requirements.filter_map { |r| r.fetch(:requirement) }
                .flat_map { |req_str| requirement_class.requirements_array(req_str) }
                .flat_map(&:requirements)
                .reject { |req_array| req_array.first.start_with?("<") }
                .map(&:last)
                .max,
      T.nilable(T.any(String, Gem::Version))
    )
end

def version_up_to_date?

def version_up_to_date?
  return sha1_version_up_to_date? if existing_version_is_sha?
  numeric_version_up_to_date?
end

def vulnerable?

def vulnerable?
  return false if security_advisories.none?
  # Can't (currently) detect whether dependencies without a version
  # (i.e., for repos without a lockfile) are vulnerable
  return false unless dependency.version
  # Can't (currently) detect whether git dependencies are vulnerable
  return false if existing_version_is_sha?
  active_advisories.any?
end