class Dependabot::Source

def self.from_url(url_string)

def self.from_url(url_string)
  return github_enterprise_from_url(url_string) unless url_string&.match?(SOURCE_REGEX)
  captures = T.must(url_string.match(SOURCE_REGEX)).named_captures
  new(
    provider: T.must(captures.fetch("provider")),
    repo: T.must(captures.fetch("repo")).delete_suffix(".git").delete_suffix("."),
    directory: captures.fetch("directory"),
    branch: captures.fetch("branch")
  )
end

def self.github_enterprise?(base_url)

def self.github_enterprise?(base_url)
  resp = Excon.get(File.join(base_url, "status"))
  resp.status == 200 &&
    # Alternatively: resp.headers["Server"] == "GitHub.com", but this
    # currently doesn't work with development environments
    ((resp.headers["X-GitHub-Request-Id"] && !resp.headers["X-GitHub-Request-Id"]&.empty?) || false)
rescue StandardError
  false
end

def self.github_enterprise_from_url(url_string)

def self.github_enterprise_from_url(url_string)
  captures = url_string&.match(GITHUB_ENTERPRISE_SOURCE)&.named_captures
  return unless captures
  return if IGNORED_PROVIDER_HOSTS.include?(captures.fetch("host"))
  base_url = "https://#{captures.fetch('host')}"
  return unless github_enterprise?(base_url)
  new(
    provider: "github",
    repo: T.must(captures.fetch("repo")).delete_suffix(".git").delete_suffix("."),
    directory: captures.fetch("directory"),
    branch: captures.fetch("branch"),
    hostname: captures.fetch("host"),
    api_endpoint: File.join(base_url, "api", "v3")
  )
end

def default_api_endpoint(provider)

def default_api_endpoint(provider)
  case provider
  when "github" then "https://api.github.com/"
  when "bitbucket" then "https://api.bitbucket.org/2.0/"
  when "gitlab" then "https://gitlab.com/api/v4"
  when "azure" then "https://dev.azure.com/"
  when "codecommit" then nil
  else raise "Unexpected provider '#{provider}'"
  end
end

def default_hostname(provider)

def default_hostname(provider)
  case provider
  when "github" then "github.com"
  when "bitbucket" then "bitbucket.org"
  when "gitlab" then "gitlab.com"
  when "azure" then "dev.azure.com"
  when "codecommit" then "us-east-1"
  else raise "Unexpected provider '#{provider}'"
  end
end

def initialize(provider:, repo:, directory: nil, directories: nil, branch: nil, commit: nil,

def initialize(provider:, repo:, directory: nil, directories: nil, branch: nil, commit: nil,
               hostname: nil, api_endpoint: nil)
  if (hostname.nil? ^ api_endpoint.nil?) && (provider != "codecommit")
    msg = "Both hostname and api_endpoint must be specified if either " \
          "are. Alternatively, both may be left blank to use the " \
          "provider's defaults."
    raise msg
  end
  @provider = provider
  @repo = repo
  @directory = directory
  @directories = directories
  @branch = branch
  @commit = commit
  @hostname = T.let(hostname || default_hostname(provider), String)
  @api_endpoint = T.let(api_endpoint || default_api_endpoint(provider), T.nilable(String))
end

def organization

def organization
  T.must(repo.split("/").first)
end

def project

def project
  raise "Project is an Azure DevOps concept only" unless provider == "azure"
  parts = repo.split("/_git/")
  return T.must(T.must(parts.first).split("/").last) if parts.first&.split("/")&.count == 2
  T.must(parts.last)
end

def unscoped_repo

def unscoped_repo
  T.must(repo.split("/").last)
end

def url

def url
  "https://" + hostname + "/" + repo
end

def url_with_directory

def url_with_directory
  return url if [nil, ".", "/"].include?(directory)
  case provider
  when "github", "gitlab"
    path = Pathname.new(File.join("tree/#{branch || 'HEAD'}", directory))
                   .cleanpath.to_path
    url + "/" + path
  when "bitbucket"
    path = Pathname.new(File.join("src/#{branch || 'default'}", directory))
                   .cleanpath.to_path
    url + "/" + path
  when "azure"
    url + "?path=#{directory}"
  when "codecommit"
    raise "The codecommit provider does not utilize URLs"
  else raise "Unexpected repo provider '#{provider}'"
  end
end