module Stories
  class FeedsController < ApplicationController
    respond_to :json

    before_action :current_user_by_token, only: [:show]

    def show
      @page = (params[:page] || 1).to_i
      # This most recent test has concluded with a winner. Preserved as a comment awaiting next test
      # @comments_variant = field_test(:comments_to_display_20240129, participant: @user)
      @comments_variant = "more_inclusive_recent_good_comments"

      # Preload frequently accessed settings to avoid N+1 queries
      preload_common_settings

      @stories = assign_feed_stories

      # After getting stories, preload settings for any additional subforems if in root
      preload_additional_subforem_settings

      add_pinned_article

      # Add edge cache headers for signed out users only
      return if user_signed_in?

      set_cache_control_headers(60) # 1 minute expiration
    end

    private

    def add_pinned_article
      return if params[:timeframe].present?

      pinned_article = PinnedArticle.get
      return if pinned_article.nil? || @stories.detect { |story| story.id == pinned_article.id }

      @stories.prepend(pinned_article.decorate)
    end

    def assign_feed_stories
      params[:type_of] = "discover" if params[:type_of].blank?

      stories = if params[:timeframe].in?(Timeframe::FILTER_TIMEFRAMES)
                  timeframe_feed
                elsif params[:type_of] == "following" && user_signed_in? && params[:timeframe] == Timeframe::LATEST_TIMEFRAME
                  latest_following_feed
                elsif params[:type_of] == "following" && user_signed_in?
                  relevant_following_feed
                elsif params[:timeframe] == Timeframe::LATEST_TIMEFRAME
                  latest_feed
                elsif user_signed_in?
                  signed_in_base_feed
                else
                  signed_out_base_feed
                end

      ArticleDecorator.decorate_collection(stories)
    end

    def signed_in_base_feed
      feed_strategy = params[:mode] || Settings::UserExperience.feed_strategy
      feed = if feed_strategy == "basic" && params[:type_of] != "following"
               Articles::Feeds::Basic.new(user: current_user, page: @page, tag: params[:tag])
             elsif feed_strategy == "configured" && params[:type_of] != "following"

               @feed_config = if params[:item]
                                FeedConfig.find_by(id: params[:item]) || FeedConfig.order("feed_success_score DESC").limit(rand(15)).sample || FeedConfig.first_or_create
                              elsif rand(20) == 0 # 5% of the time, we'll just pick a random feed config
                                FeedConfig.where("feed_impressions_count < 100").order("RANDOM()").limit(1).first || FeedConfig.order("feed_success_score DESC").limit(rand(15)).sample || FeedConfig.first_or_create
                              else
                                FeedConfig.order("feed_success_score DESC").limit(rand(15)).sample || FeedConfig.first_or_create
                              end
               Articles::Feeds::Custom.new(user: current_user, page: @page, tag: params[:tag],
                                           feed_config: @feed_config)
             else
               Articles::Feeds.feed_for(
                 user: current_user,
                 controller: self,
                 page: @page,
                 tag: params[:tag],
                 number_of_articles: 35,
                 type_of: params[:type_of] || "discover",
               )
             end
      # Hey, why the to_a you say?  Because the
      # LargeForemExperimental has already done this.  But the
      # weighted strategy has not.  I also don't want to alter the
      # weighted query implementation as it returns a lovely
      # ActiveRecord::Relation.  So this is a compromise.

      feed.more_comments_minimal_weight_randomized(comments_variant: @comments_variant)
    end

    def signed_out_base_feed
      feed = if Settings::UserExperience.feed_strategy == "basic"
               Articles::Feeds::Basic.new(user: nil, page: @page, tag: params[:tag])
             else
               Articles::Feeds.feed_for(
                 user: current_user,
                 controller: self,
                 page: @page,
                 tag: params[:tag],
                 number_of_articles: 25,
                 type_of: "discover",
               )
             end
      Datadog::Tracing.trace("feed.query",
                             span_type: "db",
                             resource: "#{self.class}.#{__method__}",
                             tags: { feed_class: feed.class.to_s.dasherize }) do
        # Hey, why the to_a you say?  Because the
        # LargeForemExperimental has already done this.  But the
        # weighted strategy has not.  I also don't want to alter the
        # weighted query implementation as it returns a lovely
        # ActiveRecord::Relation.  So this is a compromise.
        feed.default_home_feed(user_signed_in: false).to_a
      end
    end

    def timeframe_feed
      Articles::Feeds::Timeframe.call(params[:timeframe], tag: params[:tag], page: @page)
    end

    def latest_feed
      Articles::Feeds::Latest.call(tag: params[:tag], page: @page)
    end

    def latest_following_feed
      activity = current_user.user_activity
      user_ids = activity&.alltime_users || current_user.cached_following_users_ids
      organization_ids = activity&.alltime_organizations || current_user.cached_following_organizations_ids
      left_scope  = Article.published.from_subforem.where(user_id: user_ids)
      right_scope = Article.published.from_subforem.where(organization_id: organization_ids)

      @articles = left_scope
        .or(right_scope)
        .where("score > -10")
        .order("published_at DESC")
        .page(@page)
        .per(25)
    end

    def relevant_following_feed
      activity = current_user.user_activity
      user_ids = activity&.alltime_users || current_user.cached_following_users_ids
      organization_ids = activity&.alltime_organizations || current_user.cached_following_organizations_ids
      left_scope  = Article.published.from_subforem.where(user_id: user_ids)
      right_scope = Article.published.from_subforem.where(organization_id: organization_ids)

      @articles = left_scope
        .or(right_scope)
        .where("score > -10")
        .order("hotness_score DESC")
        .page(@page)
        .per(25)
    end

    def preload_common_settings
      # Preload frequently accessed settings to avoid N+1 queries from waterfall analysis
      # This addresses the multiple site_configs queries we see in the performance trace
      current_subforem_id = RequestStore.store[:subforem_id]
      root_subforem_id = RequestStore.store[:root_subforem_id]

      if current_subforem_id == root_subforem_id
        # We're in root subforem - need to handle multiple subforems dynamically
        # We'll cache settings for the most common subforems to avoid repeated queries
        @cached_subforem_logos = {}
        @cached_logo_png = Settings::General.logo_png(subforem_id: current_subforem_id)
      else
        # Single subforem - simple caching
        @cached_logo_png = Settings::General.logo_png(subforem_id: current_subforem_id)
        @cached_subforem_logo = @cached_logo_png
      end
    end

    def preload_additional_subforem_settings
      # Only needed when we're in root subforem and have multiple subforems in the feed
      return unless @cached_subforem_logos && @stories.any?

      # Get unique subforem IDs from the stories
      unique_subforem_ids = @stories.map(&:subforem_id).uniq.compact
      current_subforem_id = RequestStore.store[:subforem_id]

      # Preload settings for all unique subforems (excluding current one which is already cached)
      unique_subforem_ids.each do |subforem_id|
        next if subforem_id == current_subforem_id

        @cached_subforem_logos[subforem_id] ||= Settings::General.logo_png(subforem_id: subforem_id)
      end
    end
  end
end
