class CategoryList
  include ActiveModel::Serialization

  attr_accessor :categories,
                :topic_users,
                :uncategorized,
                :draft,
                :draft_key,
                :draft_sequence

  def initialize(guardian=nil)
    @guardian = guardian || Guardian.new

    find_relevant_topics
    find_categories

    prune_empty
    add_uncategorized
    find_user_data
  end

  private

    # Retrieve a list of all the topics we'll need
    def find_relevant_topics
      @topics_by_category_id = {}
      category_featured_topics = CategoryFeaturedTopic.select([:category_id, :topic_id]).order(:rank)
      @topics_by_id = {}

      @all_topics = Topic.where(id: category_featured_topics.map(&:topic_id))
      @all_topics.each do |t|
        @topics_by_id[t.id] = t
      end

      category_featured_topics.each do |cft|
        @topics_by_category_id[cft.category_id] ||= []
        @topics_by_category_id[cft.category_id] << cft.topic_id
      end
    end

    # Find a list of all categories to associate the topics with
    def find_categories
      @categories = Category
                      .includes(:featured_users)
                      .secured(@guardian)
                      .order('COALESCE(categories.topics_week, 0) DESC')
                      .order('COALESCE(categories.topics_month, 0) DESC')
                      .order('COALESCE(categories.topics_year, 0) DESC')

      @categories = @categories.to_a
      @categories.each do |c|
        topics_in_cat = @topics_by_category_id[c.id]
        if topics_in_cat.present?
          c.displayable_topics = []
          topics_in_cat.each do |topic_id|
            topic = @topics_by_id[topic_id]
            if topic.present?
              topic.category = c
              c.displayable_topics << topic
            end
          end
        end
      end
    end

    # Add the uncategorized "magic" category
    def add_uncategorized
      # Support for uncategorized topics
      uncategorized_topics = Topic
                        .listable_topics
                        .visible
                        .where(category_id: nil)
                        .topic_list_order
                        .limit(SiteSetting.category_featured_topics)
      if uncategorized_topics.present?

        totals = Topic.exec_sql("SELECT SUM(CASE WHEN created_at >= (CURRENT_TIMESTAMP - INTERVAL '1 WEEK') THEN 1 ELSE 0 END) as topics_week,
                                        SUM(CASE WHEN created_at >= (CURRENT_TIMESTAMP - INTERVAL '1 MONTH') THEN 1 ELSE 0 END) as topics_month,
                                        SUM(CASE WHEN created_at >= (CURRENT_TIMESTAMP - INTERVAL '1 YEAR') THEN 1 ELSE 0 END) as topics_year,
                                        COUNT(*) AS topic_count
                                 FROM topics
                                 WHERE topics.visible
                                  AND topics.deleted_at IS NULL
                                  AND topics.category_id IS NULL
                                  AND topics.archetype <> '#{Archetype.private_message}'").first


        uncategorized = Category.new({name: SiteSetting.uncategorized_name,
                                      slug: Slug.for(SiteSetting.uncategorized_name),
                                      color: SiteSetting.uncategorized_color,
                                      text_color: SiteSetting.uncategorized_text_color,
                                      featured_topics: uncategorized_topics}.merge(totals))

        # Find the appropriate place to insert it:
        insert_at = nil
        @categories.each_with_index do |c, idx|
          if (uncategorized.topics_week || 0) > (c.topics_week || 0)
            insert_at = idx
            break
          end
        end

        @categories.insert(insert_at || @categories.size, uncategorized)
      end

      if @all_topics.present? && uncategorized.present?
        uncategorized.displayable_topics = uncategorized_topics
        @all_topics << uncategorized_topics
        @all_topics.flatten!
      end
    end

    # Remove any empty topics unless we can create them (so we can see the controls)
    def prune_empty
      unless @guardian.can_create?(Category)
        # Remove categories with no featured topics unless we have the ability to edit one
        @categories.delete_if { |c| c.displayable_topics.blank? }
      end
    end

    # Get forum topic user records if appropriate
    def find_user_data
      if @guardian.current_user && @all_topics.present?
        topic_lookup = TopicUser.lookup_for(@guardian.current_user, @all_topics)

        # Attach some data for serialization to each topic
        @all_topics.each { |ft| ft.user_data = topic_lookup[ft.id] }
      end
    end
end
