module Articles
  class Suggest
    MAX_DEFAULT = 4

    def self.call(article, max: MAX_DEFAULT)
      new(article, max: max).call
    end

    def initialize(article, max: MAX_DEFAULT)
      @article = article
      @max = max
      @total_articles_count = Article.published.from_subforem.estimated_count
    end

    def call
      if cached_tag_list_array.any?
        # avoid loading more data if we don't need to
        tagged_suggestions = suggestions_by_tag(max: max)
        return tagged_suggestions if tagged_suggestions.size == max

        # if there are not enough tagged articles, load other suggestions
        # ignoring tagged articles that might be relevant twice, hence avoiding duplicates
        num_remaining_needed = max - tagged_suggestions.size
        other_articles = other_suggestions(
          max: num_remaining_needed,
          ids_to_ignore: tagged_suggestions.map(&:id),
        )
        tagged_suggestions.union(other_articles)
      else
        other_suggestions
      end
    end

    private

    attr_reader :article, :max, :total_articles_count

    def other_suggestions(max: MAX_DEFAULT, ids_to_ignore: [])
      ids_to_ignore << article.id
      Article.published.where(subforem_id: article.subforem_id)
        .where("published_at > ?", 3.months.ago)
        .where.not(id: ids_to_ignore)
        .not_authored_by(article.user_id)
        .order(hotness_score: :desc)
        .offset(rand(0..offset))
        .where("score > ?", Settings::UserExperience.index_minimum_score)
        .select(:id, :title, :path, :cached_tag_list, :cached_user, :main_image, :published, :published_at, :crossposted_at)
        .first(max)
    end

    def suggestions_by_tag(max: MAX_DEFAULT)
      Article
        .published.where(subforem_id: article.subforem_id)
        .where("published_at > ?", 3.months.ago)
        .cached_tagged_with_any(cached_tag_list_array)
        .not_authored_by(article.user_id)
        .where(tag_suggestion_query)
        .order(hotness_score: :desc)
        .offset(rand(0..offset))
        .where("score > ?", Settings::UserExperience.index_minimum_score)
        .select(:id, :title, :path, :cached_tag_list, :cached_user, :main_image, :published, :published_at, :crossposted_at)
        .first(max)
    end

    def offset
      total_articles_count > 1000 ? 200 : (total_articles_count / 10)
    end

    def tag_suggestion_query
      # Fore big communities like DEV we can look at organic page views for indicator.
      # For smaller communities, we'll a basic score check.
      total_articles_count > 1000 ? "organic_page_views_past_month_count > 5" : "score > 1"
    end

    def cached_tag_list_array
      (article.cached_tag_list || "").split(", ")
    end
  end
end
