# frozen_string_literal: true

require 'digest/md5'

MESSAGE = <<MARKDOWN
## Reviewer roulette

Changes that require review have been detected! A merge request is normally
reviewed by both a reviewer and a maintainer.
MARKDOWN

CATEGORY_TABLE_HEADER = <<MARKDOWN

To spread load more evenly across eligible reviewers, Danger has picked a
candidate for each review slot, based on their timezone. Feel free to
[override these selections](https://about.gitlab.com/handbook/engineering/projects/#gitlab) 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). You may
consider assigning a reviewer or maintainer who is
a [domain expert](https://about.gitlab.com/handbook/engineering/projects/#gitlab) for
the `DeclarativePolicy` library.

Once you've decided who will review this merge request, assign them as a reviewer!
Danger does not automatically notify them for you.

| Category | Reviewer | Maintainer |
| -------- | -------- | ---------- |
MARKDOWN

UNKNOWN_FILES_MESSAGE = <<MARKDOWN

These files couldn't be categorised, so Danger was unable to suggest a reviewer.
Please consider creating a merge request
to [add support](https://gitlab.com/gitlab-org/gitlab/blob/master/tooling/danger/project_helper.rb)
for them.
MARKDOWN

OPTIONAL_REVIEW_TEMPLATE = '%{role} review is optional for %{category}'
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

  format(NOT_AVAILABLE_TEMPLATE, role: role)
end

def note_for_spin_role(spin, role)
  if spin.optional_role == role
    return format(OPTIONAL_REVIEW_TEMPLATE, role: role.capitalize, category: helper.label_for_category(spin.category))
  end

  spin.public_send(role)&.markdown_name(author: roulette.team_mr_author) # rubocop:disable GitlabSecurity/PublicSend
end

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

  "| #{helper.label_for_category(category)} | #{reviewer_note} | #{maintainer_note} |"
end

changes = project_helper.changes_by_category

# Ignore any files that are known but uncategorized. Prompt for any unknown files
changes.delete(:none)
# To reinstate roulette for documentation, remove this line.
changes.delete(:docs)
# No special review for changelog needed and changelog was never a label.
changes.delete(:changelog)
# No special review for feature flags needed.
changes.delete(:feature_flag)
categories = changes.keys.to_set.delete(:unknown)

if changes.any?
  project = project_helper.project_name

  random_roulette_spins = roulette.spin(project, categories, timezone_experiment: false)

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

  markdown(MESSAGE)
  markdown(CATEGORY_TABLE_HEADER + rows.join("\n")) unless rows.empty?

  unknown = changes.fetch(:unknown, [])
  markdown(UNKNOWN_FILES_MESSAGE + helper.markdown_list(unknown)) unless unknown.empty?
end
