# frozen_string_literal: true

RSpec.describe Jobs::GenerateConceptsFromPopularItems do
  fab!(:topic) { Fabricate(:topic, posts_count: 6, views: 150, like_count: 12) }
  fab!(:post) { Fabricate(:post, like_count: 8, post_number: 2) }

  before do
    SiteSetting.inferred_concepts_enabled = true
    SiteSetting.inferred_concepts_daily_topics_limit = 20
    SiteSetting.inferred_concepts_daily_posts_limit = 30
    SiteSetting.inferred_concepts_min_posts = 5
    SiteSetting.inferred_concepts_min_likes = 10
    SiteSetting.inferred_concepts_min_views = 100
    SiteSetting.inferred_concepts_post_min_likes = 5
    SiteSetting.inferred_concepts_lookback_days = 30
    SiteSetting.inferred_concepts_background_match = false
  end

  describe "#execute" do
    it "does nothing when inferred_concepts_enabled is false" do
      SiteSetting.inferred_concepts_enabled = false

      expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).not_to receive(
        :find_candidate_topics,
      )
      expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).not_to receive(
        :find_candidate_posts,
      )
      allow(Jobs).to receive(:enqueue)

      subject.execute({})
    end

    it "processes popular topics when enabled" do
      candidate_topics = [topic]

      freeze_time do
        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
          :find_candidate_topics,
        ).with(
          limit: 20,
          min_posts: 5,
          min_likes: 10,
          min_views: 100,
          created_after: 30.days.ago,
        ).and_return(candidate_topics)

        allow(Jobs).to receive(:enqueue).with(
          :generate_inferred_concepts,
          item_type: "topics",
          item_ids: [topic.id],
          batch_size: 10,
        )

        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
          :find_candidate_posts,
        ).and_return([])

        subject.execute({})
      end
    end

    it "processes popular posts when enabled" do
      candidate_posts = [post]

      freeze_time do
        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
          :find_candidate_topics,
        ).and_return([])

        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
          :find_candidate_posts,
        ).with(
          limit: 30,
          min_likes: 5,
          exclude_first_posts: true,
          created_after: 30.days.ago,
        ).and_return(candidate_posts)

        allow(Jobs).to receive(:enqueue).with(
          :generate_inferred_concepts,
          item_type: "posts",
          item_ids: [post.id],
          batch_size: 10,
        )

        subject.execute({})
      end
    end

    it "schedules background matching jobs when enabled" do
      SiteSetting.inferred_concepts_background_match = true

      candidate_topics = [topic]
      candidate_posts = [post]

      expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
        :find_candidate_topics,
      ).and_return(candidate_topics)
      expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
        :find_candidate_posts,
      ).and_return(candidate_posts)

      # Expect generation jobs
      allow(Jobs).to receive(:enqueue).with(
        :generate_inferred_concepts,
        item_type: "topics",
        item_ids: [topic.id],
        batch_size: 10,
      )

      allow(Jobs).to receive(:enqueue).with(
        :generate_inferred_concepts,
        item_type: "posts",
        item_ids: [post.id],
        batch_size: 10,
      )

      # Expect background matching jobs
      allow(Jobs).to receive(:enqueue_in).with(
        1.hour,
        :generate_inferred_concepts,
        item_type: "topics",
        item_ids: [topic.id],
        batch_size: 10,
        match_only: true,
      )

      allow(Jobs).to receive(:enqueue_in).with(
        1.hour,
        :generate_inferred_concepts,
        item_type: "posts",
        item_ids: [post.id],
        batch_size: 10,
        match_only: true,
      )

      subject.execute({})
    end

    it "does not schedule jobs when no candidates found" do
      expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
        :find_candidate_topics,
      ).and_return([])
      expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
        :find_candidate_posts,
      ).and_return([])

      allow(Jobs).to receive(:enqueue)
      allow(Jobs).to receive(:enqueue_in)

      subject.execute({})
    end

    it "uses site setting values for topic filtering" do
      SiteSetting.inferred_concepts_daily_topics_limit = 50
      SiteSetting.inferred_concepts_min_posts = 8
      SiteSetting.inferred_concepts_min_likes = 15
      SiteSetting.inferred_concepts_min_views = 200
      SiteSetting.inferred_concepts_lookback_days = 45

      freeze_time do
        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
          :find_candidate_topics,
        ).with(
          limit: 50,
          min_posts: 8,
          min_likes: 15,
          min_views: 200,
          created_after: 45.days.ago,
        ).and_return([])

        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
          :find_candidate_posts,
        ).and_return([])

        subject.execute({})
      end
    end

    it "uses site setting values for post filtering" do
      SiteSetting.inferred_concepts_daily_posts_limit = 40
      SiteSetting.inferred_concepts_post_min_likes = 8
      SiteSetting.inferred_concepts_lookback_days = 45

      freeze_time do
        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
          :find_candidate_topics,
        ).and_return([])

        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
          :find_candidate_posts,
        ).with(
          limit: 40,
          min_likes: 8,
          exclude_first_posts: true,
          created_after: 45.days.ago,
        ).and_return([])

        subject.execute({})
      end
    end

    it "handles nil site setting values gracefully" do
      SiteSetting.inferred_concepts_daily_topics_limit = nil
      SiteSetting.inferred_concepts_daily_posts_limit = nil
      SiteSetting.inferred_concepts_min_posts = nil
      SiteSetting.inferred_concepts_min_likes = nil
      SiteSetting.inferred_concepts_min_views = nil
      SiteSetting.inferred_concepts_post_min_likes = nil
      # Keep lookback_days at default so .days.ago doesn't fail

      freeze_time do
        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
          :find_candidate_topics,
        ).with(
          limit: 0, # nil becomes 0
          min_posts: 0, # nil becomes 0
          min_likes: 0, # nil becomes 0
          min_views: 0, # nil becomes 0
          created_after: 30.days.ago, # default from before block
        ).and_return([])

        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
          :find_candidate_posts,
        ).with(
          limit: 0, # nil becomes 0
          min_likes: 0, # nil becomes 0
          exclude_first_posts: true,
          created_after: 30.days.ago, # default from before block
        ).and_return([])

        subject.execute({})
      end
    end

    it "processes both topics and posts in the same run" do
      candidate_topics = [topic]
      candidate_posts = [post]

      expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
        :find_candidate_topics,
      ).and_return(candidate_topics)
      expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
        :find_candidate_posts,
      ).and_return(candidate_posts)

      allow(Jobs).to receive(:enqueue).twice

      subject.execute({})
    end
  end

  context "when scheduling the job" do
    it "is scheduled to run daily" do
      expect(described_class.every).to eq(1.day)
    end
  end
end
