# frozen_string_literal: true

PROJECT_NAME = helper.config.project_name

MESSAGE = <<MARKDOWN
## Reviewer roulette

Changes that require review have been detected! A merge request is normally
reviewed by both a reviewer and a maintainer in its primary category and by a
maintainer in all other categories.
MARKDOWN

TABLE_MARKDOWN = <<MARKDOWN

To spread load more evenly across eligible reviewers, Danger has picked a candidate for each
review slot. Feel free to
[override these selections](https://about.gitlab.com/handbook/engineering/projects/##{PROJECT_NAME})
if you think someone else would be better-suited
or use the [GitLab Review Workload Dashboard](https://gitlab-org.gitlab.io/gitlab-roulette/) to find other available reviewers.

To read more on how to use the reviewer roulette, please take a look at the
[Engineering workflow](https://about.gitlab.com/handbook/engineering/workflow/#basics)
and [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html).
Please consider assigning a reviewer or maintainer who is a
[domain expert](https://about.gitlab.com/handbook/engineering/projects/#gitlab-development-kit) in the area of the merge request.

Once you've decided who will review this merge request, mention them as you
normally would! Danger does not automatically notify them for you.

MARKDOWN

TABLE_HEADER_WITH_CATEGORIES = <<MARKDOWN
| Category | Reviewer | Maintainer |
| -------- | -------- | ---------- |
MARKDOWN

TABLE_HEADER_WITHOUT_CATEGORIES = <<MARKDOWN
| Reviewer | Maintainer |
| -------- | ---------- |
MARKDOWN

OPTIONAL_REVIEW_TEMPLATE = '%{role} review is optional'
NOT_AVAILABLE_TEMPLATE = 'No %{role} available'

def note_for_spins_role(spins, role)
  spins.each do |spin|
    note = note_for_spin_role(spin, role)

    return note if note
  end

  NOT_AVAILABLE_TEMPLATE % { role: role }
end

def note_for_spin_role(spin, role)
  if spin.optional_role == role
    return OPTIONAL_REVIEW_TEMPLATE % { role: role.capitalize }
  end

  spin.public_send(role)&.markdown_name(author: roulette.team_mr_author)
end

def markdown_row_for_spins(category = nil, spins_array, has_categories:)
  reviewer_note = note_for_spins_role(spins_array, :reviewer)
  maintainer_note = note_for_spins_role(spins_array, :maintainer)

  row = +"| #{reviewer_note} | #{maintainer_note} |"
  row.prepend("| #{helper.label_for_category(category)} ") if has_categories
  row
end

changes = helper.changes_by_category

# Replicating label based categories from:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/danger/roulette/Dangerfile
categories = Set.new(changes.keys - [:unknown])

# Ensure to spin for database reviewer/maintainer when ~database is applied (e.g. to review SQL queries)
categories << :database if helper.mr_labels.include?('database')

# Ensure to spin for UX reviewer when ~UX is applied (e.g. to review changes to the UI) except when it's from wider community contribution where we want to assign from the corresponding group
categories << :ux if helper.mr_labels.include?('UX') && !helper.mr_labels.include?('Community contribution')

# Ensure to spin for Product Intelligence reviewer when ~"product intelligence::review pending" is applied
categories << :product_intelligence if helper.mr_labels.include?("product intelligence::review pending")

# Skip Product intelligence reviews for growth experiment MRs
categories.delete(:product_intelligence) if helper.mr_labels.include?("growth experiment")

if changes.any?
  has_categories = categories.any?
  categories = [nil] unless has_categories
  random_roulette_spins = roulette.spin(nil, categories)

  rows = random_roulette_spins.map do |spin|
    markdown_row_for_spins(spin.category, [spin], has_categories: has_categories)
  end

  table_header = has_categories ? TABLE_HEADER_WITH_CATEGORIES : TABLE_HEADER_WITHOUT_CATEGORIES

  markdown(MESSAGE)
  markdown(TABLE_MARKDOWN + table_header + rows.join("\n")) unless rows.empty?
end