# frozen_string_literal: true

module Danger
  # This plugin checks whether a pull request is assigned to a milestone and whether the milestone's due date is approaching.
  #
  # @example Check if a milestone is set
  #
  #          # Check if PR is assigned to a milestone
  #          checker.check_milestone_set
  #
  #          # Check if PR is assigned to a milestone, reporting an error if that's not the case
  #          checker.check_milestone_set(report_type: :error)
  #
  # @example Run a milestone check
  #
  #          # Check if milestone due date is approaching, reporting a warning if the milestone is in less than 5 days
  #          checker.check_milestone_due_date(days_before_due: 5)
  #
  # @example Run a milestone check with custom parameters
  #
  #          # Check if milestone due date is within 3 days, reporting an error if the due date has passed and in case there's no milestone set
  #          checker.check_milestone_due_date(days_before_due: 3, report_type: :error, report_type_if_no_milestone: :error)
  #
  # @example Run a milestone check with a custom milestone behaviour parameter
  #
  #          # Check if milestone due date is approaching and don't report anything if no milestone is assigned
  #          checker.check_milestone_due_date(report_type_if_no_milestone: :none)
  #
  # @see Automattic/dangermattic
  # @tags milestone, github, process
  #
  class MilestoneChecker < Plugin
    # Checks if the pull request is assigned to a milestone.
    #
    # @return [void]
    def check_milestone_set(report_type: :warning)
      return unless milestone.nil?

      message = 'PR is not assigned to a milestone.'
      reporter.report(message: message, type: report_type)
    end

    # Checks if the pull request's milestone is due to finish within a certain number of days.
    #
    # @param days_before_due [Integer] Number of days before the milestone due date for the check to apply.
    # @param report_type [Symbol] (optional) The type of message for when the PR is has passed over the `days_before_due` threshold. Types: :error, :warning (default), :message.
    # @param report_type_if_no_milestone [Symbol] The type of message for when the PR is not assigned to a milestone. Types: :error, :warning (default), :message. You can also pass :none to not leave a message when there is no milestone.
    #
    # @return [void]
    def check_milestone_due_date(days_before_due:, report_type: :warning, report_type_if_no_milestone: :warning)
      if milestone.nil?
        check_milestone_set(report_type: report_type_if_no_milestone)
        return
      end

      return unless pr_state != 'closed' && milestone_due_date

      today = DateTime.now

      seconds_threshold = days_before_due * 24 * 60 * 60
      time_before_due_date = milestone_due_date.to_time.to_i - today.to_time.to_i
      return unless time_before_due_date <= seconds_threshold

      message_text = "This PR is assigned to the milestone [#{milestone_title}](#{milestone_url}). "
      message_text += if time_before_due_date.positive?
                        "This milestone is due in less than #{days_before_due} days.\n" \
                          'Please make sure to get it merged by then or assign it to a milestone with a later deadline.'
                      else
                        "The due date for this milestone has already passed.\n" \
                          'Please assign it to a milestone with a later deadline or check whether the release for this milestone has already been finished.'
                      end

      reporter.report(message: message_text, type: report_type)
    end

    private

    def milestone
      github.pr_json['milestone']
    end

    def milestone_due_date
      milestone['due_on']
    end

    def milestone_title
      milestone['title']
    end

    def milestone_url
      milestone['html_url']
    end

    def pr_state
      github.pr_json['state']
    end
  end
end