# frozen_string_literal: true

RSpec.describe Jobs::GenerateInferredConcepts do
  fab!(:topic)
  fab!(:post)
  fab!(:concept) { Fabricate(:inferred_concept, name: "programming") }

  before { SiteSetting.inferred_concepts_enabled = true }

  describe "#execute" do
    it "does nothing with blank item_ids" do
      expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).not_to receive(
        :match_topic_to_concepts,
      )

      subject.execute(item_type: "topics", item_ids: [])
      subject.execute(item_type: "topics", item_ids: nil)
    end

    it "does nothing with blank item_type" do
      expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).not_to receive(
        :match_topic_to_concepts,
      )

      subject.execute(item_type: "", item_ids: [topic.id])
      subject.execute(item_type: nil, item_ids: [topic.id])
    end

    it "validates item_type to be topics or posts" do
      allow(Rails.logger).to receive(:error).with(/Invalid item_type/)

      subject.execute(item_type: "invalid", item_ids: [1])
    end

    context "with topics" do
      it "processes topics in match_only mode" do
        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
          :match_topic_to_concepts,
        ).with(topic)

        subject.execute(item_type: "topics", item_ids: [topic.id], match_only: true)
      end

      it "processes topics in generation mode" do
        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
          :generate_concepts_from_topic,
        ).with(topic)

        subject.execute(item_type: "topics", item_ids: [topic.id], match_only: false)
      end

      it "handles topics that don't exist" do
        # Non-existent IDs should be silently skipped (no error expected)
        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).not_to receive(
          :match_topic_to_concepts,
        )

        subject.execute(
          item_type: "topics",
          item_ids: [999_999], # non-existent ID
          match_only: true,
        )
      end

      it "processes multiple topics" do
        topic2 = Fabricate(:topic)

        manager_instance = instance_double(DiscourseAi::InferredConcepts::Manager)
        allow(DiscourseAi::InferredConcepts::Manager).to receive(:new).and_return(manager_instance)

        allow(manager_instance).to receive(:match_topic_to_concepts).with(topic)
        allow(manager_instance).to receive(:match_topic_to_concepts).with(topic2)

        subject.execute(item_type: "topics", item_ids: [topic.id, topic2.id], match_only: true)
      end

      it "processes topics in batches" do
        topics = Array.new(5) { Fabricate(:topic) }
        topic_ids = topics.map(&:id)

        # Should process in batches of 3
        allow(Topic).to receive(:where).with(id: topic_ids[0..2]).and_call_original
        allow(Topic).to receive(:where).with(id: topic_ids[3..4]).and_call_original

        subject.execute(item_type: "topics", item_ids: topic_ids, batch_size: 3, match_only: true)
      end
    end

    context "with posts" do
      it "processes posts in match_only mode" do
        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
          :match_post_to_concepts,
        ).with(post)

        subject.execute(item_type: "posts", item_ids: [post.id], match_only: true)
      end

      it "processes posts in generation mode" do
        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
          :generate_concepts_from_post,
        ).with(post)

        subject.execute(item_type: "posts", item_ids: [post.id], match_only: false)
      end

      it "handles posts that don't exist" do
        # Non-existent IDs should be silently skipped (no error expected)
        expect_any_instance_of(DiscourseAi::InferredConcepts::Manager).not_to receive(
          :match_post_to_concepts,
        )

        subject.execute(
          item_type: "posts",
          item_ids: [999_999], # non-existent ID
          match_only: true,
        )
      end

      it "processes multiple posts" do
        post2 = Fabricate(:post)

        manager_instance = instance_double(DiscourseAi::InferredConcepts::Manager)
        allow(DiscourseAi::InferredConcepts::Manager).to receive(:new).and_return(manager_instance)

        allow(manager_instance).to receive(:match_post_to_concepts).with(post)
        allow(manager_instance).to receive(:match_post_to_concepts).with(post2)

        subject.execute(item_type: "posts", item_ids: [post.id, post2.id], match_only: true)
      end
    end

    it "handles exceptions during processing" do
      allow_any_instance_of(DiscourseAi::InferredConcepts::Manager).to receive(
        :match_topic_to_concepts,
      ).and_raise(StandardError.new("Test error"))

      allow(Rails.logger).to receive(:error).with(
        /Error generating concepts from topic #{topic.id}/,
      )

      subject.execute(item_type: "topics", item_ids: [topic.id], match_only: true)
    end

    it "uses default batch size of 100" do
      topics = Array.new(150) { Fabricate(:topic) }
      topic_ids = topics.map(&:id)

      # Should process in batches of 100
      allow(Topic).to receive(:where).with(id: topic_ids[0..99]).and_call_original
      allow(Topic).to receive(:where).with(id: topic_ids[100..149]).and_call_original

      subject.execute(item_type: "topics", item_ids: topic_ids, match_only: true)
    end

    it "respects custom batch size" do
      topics = Array.new(5) { Fabricate(:topic) }
      topic_ids = topics.map(&:id)

      # Should process in batches of 2
      allow(Topic).to receive(:where).with(id: topic_ids[0..1]).and_call_original
      allow(Topic).to receive(:where).with(id: topic_ids[2..3]).and_call_original
      allow(Topic).to receive(:where).with(id: topic_ids[4..4]).and_call_original

      subject.execute(item_type: "topics", item_ids: topic_ids, batch_size: 2, match_only: true)
    end
  end
end
