# Reaction uses this class to calculate points before saving
class CalculateReactionPoints
  DEFAULT_SCORE = 1.0

  POINTS = {
    "invalid" => 0,
    "User" => 2,
    "confirmed" => 2,
    "positive" => 5,
    "negative" => -5
  }.freeze

  # Days to ramp up new user points weight
  NEW_USER_RAMPUP_DAYS_COUNT = 10

  def self.call(reaction)
    new(reaction).calculate_points
  end

  def initialize(reaction)
    @reaction = reaction
  end

  def calculate_points
    base_points = reaction_category_score

    # Adjust for certain states
    base_points = POINTS["invalid"] if status == "invalid"
    base_points /= POINTS["User"] if reactable_type == "User"
    base_points *= POINTS["confirmed"] if status == "confirmed"
    if base_points.positive? && user&.base_subscriber? && reactable.respond_to?(:user_id) && reactable&.user_id != user_id
      base_points += Settings::UserExperience.index_minimum_score
    end

    unless persisted? # Actions we only want to apply upon initial creation
      # Author's comment reaction counts for more weight on to their own posts. (5.0 vs 1.0)
      base_points *= POINTS["positive"] if positive_reaction_to_comment_on_own_article?

      # New users will have their reaction weight gradually ramp by 0.1 from 0 to 1.0.
      base_points *= new_user_adjusted_points if new_untrusted_user # New users get minimal reaction weight
    end

    user ? (base_points * user.reputation_modifier) : POINTS["negative"]
  end

  attr_reader :reaction

  delegate :category, :persisted?, :positive?, :reactable, :reactable_type, :status, :user, :user_id,
           to: :reaction

  private

  def reaction_category
    ReactionCategory[category]
  end

  def reaction_category_score
    reaction_category&.score || DEFAULT_SCORE
  end

  def new_untrusted_user
    user.registered_at > NEW_USER_RAMPUP_DAYS_COUNT.days.ago && !user.trusted? && !user.any_admin?
  end

  def new_user_adjusted_points
    ((Time.current - user.registered_at).seconds.in_days / NEW_USER_RAMPUP_DAYS_COUNT)
  end

  def positive_reaction_to_comment_on_own_article?
    positive? && reaction_to_comment? && own_article?
  end

  def reaction_to_comment?
    reactable_type == "Comment"
  end

  def own_article?
    reactable&.commentable&.user_id == user_id
  end
end
