# frozen_string_literal: true

class GitHubRepository < GitHubResource
  depends_on :import

  DEFAULT_LABEL_COLOR = "ffffff"
  TEMPLATE_REPOS_API_PREVIEW = "application/vnd.github.baptiste-preview"
  TEMPLATE_PREVIEW_OPTIONS = {
    accept: TEMPLATE_REPOS_API_PREVIEW, headers: GitHub::APIHeaders.no_cache_no_store
  }.freeze

  # NOTE: LEGACY, DO NOT REMOVE.
  # This is needed for the lib/collab_migration.rb
  def add_collaborator(collaborator, options = {})
    GitHub::Errors.with_error_handling do
      @client.add_collaborator(@id, collaborator, options)
    end
  end

  # Public: Invite a user to a GitHub repository.
  #
  # user - The String GitHub login for the user.
  #
  # Returns an Integer Invitation id, or raises a GitHub::Error.
  def invite(user, **options)
    GitHub::Errors.with_error_handling do
      options[:accept] = Octokit::Preview::PREVIEW_TYPES[:repository_invitations]
      @client.invite_user_to_repository(@id, user, options)
    end
  end

  # Public: Get all labels of a GitHub repository.
  #
  # Returns a list of labels
  def labels(options = {})
    GitHub::Errors.with_error_handling do
      @client.labels(full_name, options)
    end
  rescue GitHub::Error
    []
  end

  # Public: Add a label to a GitHub repository.
  #
  # label - The String name of the label.
  # color -  (defaults to: "ffffff")  A color, in hex, without the leading #
  #
  # Returns a Hash of the label, or raises a GitHub::Error.
  def create_label(label, color = DEFAULT_LABEL_COLOR, options = {})
    GitHub::Errors.with_error_handling do
      @client.add_label(full_name, label, color, options)
    end
  end

  # Public: Delete a label to a GitHub repository.
  #
  # label - The String name of the label.
  #
  # Returns nothing, or raises a GitHub::Error.
  def delete_label!(label, options = {})
    GitHub::Errors.with_error_handling do
      @client.delete_label!(full_name, label, options)
    end
  end

  # Public: Add issues to the GitHub repository.
  #
  # title    - The title of the issue.
  # body     - The body of the issue.
  #
  # options - optional params
  #          :assignee - User login.
  #          :milestone - Milestone number.
  #          :labels - List of comma separated Label names.
  #
  # Returns the newly created issue, or raises a GitHub::Error.
  def create_issue(title, body, **options)
    GitHub::Errors.with_error_handling do
      @client.create_issue(full_name, title, body, options)
    end
  end

  # Public: Get a tree object from the GitHub repository.
  #
  # sha    - sha of the tree.
  #
  # Returns a git Tree object, or empty hash.
  def tree(sha, **options)
    GitHub::Errors.with_error_handling do
      @client.tree(full_name, sha, options)
    end
  rescue GitHub::Error
    {}
  end

  # Public: Get a blob from the GitHub repository.
  #
  # sha    - The string sha value of the blob.
  #
  # Returns a GitHubBlob instance, or raises a GitHub::Error.
  def blob(sha, **options)
    GitHub::Errors.with_error_handling do
      @blob = GitHubBlob.new(self, sha, options)
    end
  end

  def default_branch
    GitHub::Errors.with_error_handling do
      repository = @client.repository(full_name)

      repository[:default_branch]
    end
  end

  def branch(name, **options)
    GitHub::Errors.with_error_handling do
      @client.branch(full_name, name, options)
    end
  rescue GitHub::Error
    {}
  end

  # Internal: Helper to get a tree's objects of a git tree
  #
  # tree_sha - The string sha of a git tree
  #
  # Returns a list of objects
  def tree_objects(tree_sha)
    git_tree = tree(tree_sha)
    return [] if git_tree.blank?
    git_tree.tree
  end

  def remove_branch(name, **options)
    GitHub::Errors.with_error_handling do
      @client.delete_branch(full_name, name, options)
    end
  end

  def commits(branch)
    GitHub::Errors.with_error_handling do
      @client.commits(full_name, sha: branch)
    end
  rescue GitHub::Error
    []
  end

  # The `commits` method paginates to 30 commits.
  # As an alternative, we fetch the contribution stats for the
  # top 100 contributors, then sum the commits.
  # This isn't perfect, but it's better than our current approach which
  # shows 30 commits for anything with more than 30 commits.
  def number_of_commits
    GitHub::Errors.with_error_handling do
      result = @client.contributors_stats(full_name, retry_timeout: 2)
      return 0 unless result

      result.sum do |user|
        user["total"]
      end
    end
  rescue GitHub::Error
    0
  end

  def empty?
    GitHub::Errors.with_error_handling do
      @client.contents(@id).empty?
    end
  rescue GitHub::Error
    return true
  end

  def commits_url(branch)
    html_url + "/commits/" + branch
  end

  def tree_url_for_sha(sha)
    html_url + "/tree/" + sha
  end

  def public?
    !private
  end

  # Public: Checks if the GitHub repository has a given branch.
  #
  # branch    - name of the branch to check for
  #
  # Returns true if branch exists, false otherwise
  def branch_present?(branch, **options)
    GitHub::Errors.with_error_handling do
      @client.branches(full_name, options).map(&:name).include? branch
    end
  rescue GitHub::Error
    false
  end

  def present?(**options)
    self.class.present?(@client, @id, options)
  end

  def public=(is_public)
    GitHub::Errors.with_error_handling do
      @client.update(full_name, private: !is_public)
    end
  end

  def self.present?(client, full_name, **options)
    GitHub::Errors.with_error_handling do
      client.repository?(full_name, options)
    end
  rescue GitHub::Error
    false
  end

  def self.find_by_name_with_owner!(client, full_name)
    GitHub::Errors.with_error_handling do
      repository = client.repository(full_name)
      GitHubRepository.new(client, repository.id)
    end
  end

  def temp_clone_url
    GitHub::Errors.with_error_handling do
      repository = client.repository(
        @id,
        accept: "application/vnd.github.daredevil-preview+json"
      )
      token = repository[:temp_clone_token]
      clone_url = URI(repository[:clone_url])
      clone_url.userinfo = "x-access-token:#{token}"

      clone_url.to_s
    end
  end

  def template?
    @client.repository(@id, TEMPLATE_PREVIEW_OPTIONS).is_template
  end

  private

  def github_attributes
    %w[name full_name html_url node_id private owner]
  end
end
