class FeedQueryService
  MEDIA_VERBS = %w[updated rated progressed].freeze
  POST_VERBS = %w[post comment follow review].freeze

  attr_reader :params, :user

  def initialize(params, user)
    @params = params
    @user = user
  end

  def list
    return @list if @list
    list = feed.activities
    list = list.page(id_lt: cursor) if cursor
    list = list.per(limit) if limit
    list = list.where_id(*id_query) if id_query
    list = list.mark(mark) if mark
    list = list.sfw if sfw_filter?
    if Flipper[:feed_following_filter].enabled?(User.current)
      list = list.only_following(user.id) if only_following?
    end
    list = list.blocking(blocked)
    list = list.select(kind_select[:ratio], &kind_select[:proc]) if kind_select
    if Flipper[:feed_reasons].enabled?(User.current)
      list = list.map(&method(:annotate_with_reason)) if feed.is_a?(TimelineFeed)
    end
    @list = list
  end

  def feed
    @feed ||= FeedRouter.route(params[:group], params[:id])
  end

  private

  def sfw_filter?
    return false if user&.sfw_filter_preference == 'nsfw_everywhere'
    # Enables the SFW filter for SFW media and global feeds (in addition to your settings)
    user&.sfw_filter? || feed.try(:media).try(:sfw?) || params[:group] == 'global'
  end

  def cursor
    params.dig(:page, :cursor)
  end

  def limit
    params.dig(:page, :limit)&.to_i
  end

  def mark
    params[:mark]
  end

  def id_query
    return unless params.dig(:filter, :id).is_a? Hash
    operator, id = params.dig(:filter, :id).to_a.flatten
    [operator.to_sym, id]
  end

  def kind_filter
    params.dig(:filter, :kind)
  end

  def kind_select
    @kind_filter ||=
      case kind_filter
      when 'media'
        {
          ratio: 0.8,
          proc: ->(act) do
            if MEDIA_VERBS.include?(act['verb'])
              true
            else
              throw :remove_group
            end
          end
        }
      when 'posts'
        {
          ratio: 0.2,
          proc: ->(act) do
            if POST_VERBS.include?(act['verb'])
              true
            else
              throw :remove_group
            end
          end
        }
      end
  end

  def only_following?
    params.dig(:filter, :following)
  end

  def followed
    @followed ||= Set.new(Follow.where(follower_id: user.id).pluck(:followed_id))
  end

  def groups
    @groups ||= Set.new(GroupMember.where(user_id: user.id).pluck(:group_id))
  end

  def annotate_with_reason(act)
    if act['target'].is_a?(Post)
      user_id = act['target'].user_id
      group_id = act['target'].target_group_id
      act['reason'] = 'media'
      act['reason'] = 'follow' if followed.include?(user_id)
      act['reason'] = 'follow' if user.id == user_id
      act['reason'] = 'group' if groups.include?(group_id)
    end
    act
  end

  def blocked
    Block.hidden_for(user)
  end
end
