lib/evil_query.rb
# frozen_string_literal: true

module EvilQuery
  class Base
    include Diaspora::Logging

    def fetch_ids!(relation, id_column)
      #the relation should be ordered and limited by here
      @class.connection.select_values(id_sql(relation, id_column))
    end

    def id_sql(relation, id_column)
      @class.connection.unprepared_statement { relation.select(id_column).to_sql }
    end
  end

  class Participation < Base
    def initialize(user)
      @user = user
      @class = Post
    end

    def posts
      author_id = @user.person_id
      Post.joins("LEFT OUTER JOIN participations ON participations.target_id = posts.id AND " \
                 "participations.target_type = 'Post'")
          .where(::Participation.arel_table[:author_id].eq(author_id).or(Post.arel_table[:author_id].eq(author_id)))
          .order("posts.interacted_at DESC")
          .distinct
    end
  end

  class LikedPosts < Base
    def initialize(user)
      @user = user
    end

    def posts
      Post.liked_by(@user.person)
    end
  end

  class CommentedPosts < Base
    def initialize(user)
      @user = user
    end

    def posts
      Post.commented_by(@user.person)
    end
  end

  class MultiStream < Base
    def initialize(user, order, max_time, include_spotlight)
      @user = user
      @class = Post
      @order = order
      @max_time = max_time
      @include_spotlight = include_spotlight
    end

    def make_relation!
      logger.debug("[EVIL-QUERY] make_relation!")
      post_ids = aspects_post_ids! + ids!(followed_tags_posts!) + ids!(mentioned_posts)
      post_ids += ids!(community_spotlight_posts!) if @include_spotlight
      Post.where(:id => post_ids)
    end

    def aspects_post_ids!
      logger.debug("[EVIL-QUERY] aspect_post_ids!")
      @user.visible_shareable_ids(Post, limit: 15, order: "#{@order} DESC", max_time: @max_time, all_aspects?: true)
    end

    def followed_tags_posts!
      logger.debug("[EVIL-QUERY] followed_tags_posts!")
      StatusMessage.public_tag_stream(@user.followed_tag_ids).excluding_hidden_content(@user)
    end

    def mentioned_posts
      logger.debug("[EVIL-QUERY] mentioned_posts")
      StatusMessage.where_person_is_mentioned(@user.person)
    end

    def community_spotlight_posts!
      Post.all_public.where(:author_id => fetch_ids!(Person.community_spotlight, 'people.id'))
    end

    def ids!(query)
      fetch_ids!(query.for_a_stream(@max_time, @order), 'posts.id')
    end
  end

  class VisibleShareableById < Base
    def initialize(user, klass, key, id, conditions={})
      @querent = user
      @class = klass
      @key = key
      @id  = id
      @conditions = conditions
    end

    def post!
      #small optimization - is this optimal order??
      querent_has_visibility.first || querent_is_author.first || public_post.first
    end

    protected

    def querent_has_visibility
      @class.where(@key => @id).joins(:share_visibilities)
        .where(share_visibilities: {user_id: @querent.id})
        .where(@conditions)
        .select(@class.table_name + ".*")
    end

    def querent_is_author
      @class.where(@key => @id, :author_id => @querent.person.id).where(@conditions)
    end

    def public_post
      @class.where(@key => @id, :public => true).where(@conditions)
    end
  end
end
