# frozen_string_literal: true

RSpec.describe TopicsBulkAction do
  fab!(:user) { Fabricate(:user, refresh_auto_groups: true) }
  fab!(:topic) { Fabricate(:topic, user: user) }

  describe "#dismiss_topics" do
    fab!(:user) { Fabricate(:user, created_at: 1.days.ago, refresh_auto_groups: true) }
    fab!(:category)
    fab!(:topic2) { Fabricate(:topic, category: category, created_at: 60.minutes.ago) }
    fab!(:topic3) { Fabricate(:topic, category: category, created_at: 120.minutes.ago) }

    before { topic.destroy! }

    it "dismisses private messages" do
      pm = Fabricate(:private_message_topic, recipient: user)

      TopicsBulkAction.new(user, [pm.id], type: "dismiss_topics").perform!

      expect(DismissedTopicUser.exists?(topic: pm)).to eq(true)
    end

    it "dismisses two topics" do
      expect {
        TopicsBulkAction.new(user, [Topic.all.pluck(:id)], type: "dismiss_topics").perform!
      }.to change { DismissedTopicUser.count }.by(2)
    end

    it "returns dismissed topic ids" do
      expect(
        TopicsBulkAction.new(user, [Topic.all.pluck(:id)], type: "dismiss_topics").perform!.sort,
      ).to match_array([topic2.id, topic3.id])
    end

    it "respects max_new_topics limit" do
      SiteSetting.max_new_topics = 1
      expect do
        TopicsBulkAction.new(user, [Topic.all.pluck(:id)], type: "dismiss_topics").perform!
      end.to change { DismissedTopicUser.count }.by(1)

      dismissed_topic_user = DismissedTopicUser.last

      expect(dismissed_topic_user.user_id).to eq(user.id)
      expect(dismissed_topic_user.topic_id).to eq(topic2.id)
      expect(dismissed_topic_user.created_at).not_to be_nil
    end

    it "respects seen topics" do
      Fabricate(:topic_user, user: user, topic: topic2, last_read_post_number: 1)
      Fabricate(:topic_user, user: user, topic: topic3, last_read_post_number: 1)
      expect do
        TopicsBulkAction.new(user, [Topic.all.pluck(:id)], type: "dismiss_topics").perform!
      end.not_to change { DismissedTopicUser.count }
    end

    it "dismisses when topic user without last_read_post_number" do
      Fabricate(:topic_user, user: user, topic: topic2, last_read_post_number: nil)
      Fabricate(:topic_user, user: user, topic: topic3, last_read_post_number: nil)
      expect do
        TopicsBulkAction.new(user, [Topic.all.pluck(:id)], type: "dismiss_topics").perform!
      end.to change { DismissedTopicUser.count }.by(2)
    end

    it "respects new_topic_duration_minutes" do
      user.user_option.update!(new_topic_duration_minutes: 70)

      expect do
        TopicsBulkAction.new(user, [Topic.all.pluck(:id)], type: "dismiss_topics").perform!
      end.to change { DismissedTopicUser.count }.by(1)

      dismissed_topic_user = DismissedTopicUser.last

      expect(dismissed_topic_user.user_id).to eq(user.id)
      expect(dismissed_topic_user.topic_id).to eq(topic2.id)
      expect(dismissed_topic_user.created_at).not_to be_nil
    end

    it "doesn't dismiss topics the user can't see" do
      group = Fabricate(:group)
      private_category = Fabricate(:private_category, group: group)
      topic2.update!(category_id: private_category.id)

      expect do
        TopicsBulkAction.new(user, [topic2.id, topic3.id], type: "dismiss_topics").perform!
      end.to change { DismissedTopicUser.count }.by(1)

      expect(DismissedTopicUser.where(user_id: user.id).pluck(:topic_id)).to eq([topic3.id])

      group.add(user)

      expect do
        TopicsBulkAction.new(user, [topic2.id, topic3.id], type: "dismiss_topics").perform!
      end.to change { DismissedTopicUser.count }.by(1)

      expect(DismissedTopicUser.where(user_id: user.id).pluck(:topic_id)).to contain_exactly(
        topic2.id,
        topic3.id,
      )
    end
  end

  describe "dismiss_posts" do
    it "dismisses posts" do
      post1 = create_post
      p = create_post(topic_id: post1.topic_id)
      create_post(topic_id: post1.topic_id)

      PostDestroyer.new(Fabricate(:admin), p).destroy

      TopicsBulkAction.new(post1.user, [post1.topic_id], type: "dismiss_posts").perform!

      tu = TopicUser.find_by(user_id: post1.user_id, topic_id: post1.topic_id)

      expect(tu.last_read_post_number).to eq(3)
    end

    context "when the user is staff" do
      fab!(:user) { Fabricate(:admin) }

      context "when the highest_staff_post_number is > highest_post_number for a topic (e.g. whisper is last post)" do
        it "dismisses posts" do
          SiteSetting.whispers_allowed_groups = "#{Group::AUTO_GROUPS[:staff]}"
          post1 = create_post(user: user)
          p = create_post(topic_id: post1.topic_id)
          create_post(topic_id: post1.topic_id)

          whisper =
            PostCreator.new(
              user,
              topic_id: post1.topic.id,
              post_type: Post.types[:whisper],
              raw: "this is a whispered reply",
            ).create

          TopicsBulkAction.new(user, [post1.topic_id], type: "dismiss_posts").perform!

          tu = TopicUser.find_by(user_id: user.id, topic_id: post1.topic_id)

          expect(tu.last_read_post_number).to eq(4)
        end
      end
    end
  end

  describe "invalid operation" do
    let(:user) { Fabricate.build(:user) }

    it "raises an error with an invalid operation" do
      tba = TopicsBulkAction.new(user, [1], type: "rm_root")
      expect { tba.perform! }.to raise_error(Discourse::InvalidParameters)
    end
  end

  describe "change_category" do
    fab!(:category)
    fab!(:fist_post) { Fabricate(:post, topic: topic) }

    context "when the user can edit the topic" do
      context "with 'create_revision_on_bulk_topic_moves' setting enabled" do
        before { SiteSetting.create_revision_on_bulk_topic_moves = true }

        it "changes the category, creates a post revision and returns the topic_id" do
          old_category_id = topic.category_id
          tba =
            TopicsBulkAction.new(
              topic.user,
              [topic.id],
              type: "change_category",
              category_id: category.id,
            )
          topic_ids = tba.perform!
          expect(topic_ids).to eq([topic.id])
          topic.reload
          expect(topic.category).to eq(category)

          revision = topic.first_post.revisions.last
          expect(revision).to be_present
          expect(revision.modifications).to eq({ "category_id" => [old_category_id, category.id] })
        end

        it "doesn't do anything when category stays the same" do
          tba =
            TopicsBulkAction.new(
              topic.user,
              [topic.id],
              type: "change_category",
              category_id: topic.category_id,
            )
          topic_ids = tba.perform!
          expect(topic_ids).to be_empty

          topic.reload
          revision = topic.first_post.revisions.last
          expect(revision).to be_nil
        end
      end

      context "with 'create_revision_on_bulk_topic_moves' setting disabled" do
        before { SiteSetting.create_revision_on_bulk_topic_moves = false }

        it "changes the category, doesn't create a post revision and returns the topic_id" do
          tba =
            TopicsBulkAction.new(
              topic.user,
              [topic.id],
              type: "change_category",
              category_id: category.id,
            )
          topic_ids = tba.perform!
          expect(topic_ids).to eq([topic.id])
          topic.reload
          expect(topic.category).to eq(category)

          revision = topic.first_post.revisions.last
          expect(revision).to be_nil
        end

        it "doesn't do anything when category stays the same" do
          tba =
            TopicsBulkAction.new(
              topic.user,
              [topic.id],
              type: "change_category",
              category_id: topic.category_id,
            )
          topic_ids = tba.perform!
          expect(topic_ids).to be_empty
        end
      end
    end

    context "when the user can't edit the topic" do
      it "doesn't change the category" do
        Guardian.any_instance.expects(:can_edit?).returns(false)
        tba =
          TopicsBulkAction.new(
            topic.user,
            [topic.id],
            type: "change_category",
            category_id: category.id,
          )
        topic_ids = tba.perform!
        expect(topic_ids).to eq([])
        topic.reload
        expect(topic.category).not_to eq(category)
      end
    end
  end

  describe "destroy_post_timing" do
    fab!(:fist_post) { Fabricate(:post, topic: topic) }

    before { PostTiming.process_timings(topic.user, topic.id, 10, [[1, 10]]) }

    it "delegates to PostTiming.destroy_for" do
      tba = TopicsBulkAction.new(topic.user, [topic.id], type: "destroy_post_timing")
      topic_ids = nil
      expect { topic_ids = tba.perform! }.to change { PostTiming.count }.by(-1)
      expect(topic_ids).to contain_exactly(topic.id)
    end
  end

  describe "delete" do
    fab!(:topic) { Fabricate(:post).topic }
    fab!(:moderator)

    it "deletes the topic" do
      tba = TopicsBulkAction.new(moderator, [topic.id], type: "delete")
      tba.perform!
      topic.reload
      expect(topic).to be_trashed
    end
  end

  describe "change_notification_level" do
    context "when the user can see the topic" do
      it "updates the notification level" do
        tba =
          TopicsBulkAction.new(
            topic.user,
            [topic.id],
            type: "change_notification_level",
            notification_level_id: 2,
          )
        topic_ids = tba.perform!
        expect(topic_ids).to eq([topic.id])
        expect(TopicUser.get(topic, topic.user).notification_level).to eq(2)
      end
    end

    context "when the user can't see the topic" do
      it "doesn't change the level" do
        Guardian.any_instance.expects(:can_see?).returns(false)
        tba =
          TopicsBulkAction.new(
            topic.user,
            [topic.id],
            type: "change_notification_level",
            notification_level_id: 2,
          )
        topic_ids = tba.perform!
        expect(topic_ids).to eq([])
        expect(TopicUser.get(topic, topic.user)).to be_blank
      end
    end
  end

  describe "close" do
    context "when the user can moderate the topic" do
      it "closes the topic and returns the topic_id" do
        Guardian.any_instance.expects(:can_moderate?).returns(true)
        Guardian.any_instance.expects(:can_create?).returns(true)
        tba = TopicsBulkAction.new(topic.user, [topic.id], type: "close")
        topic_ids = tba.perform!
        expect(topic_ids).to eq([topic.id])
        topic.reload
        expect(topic).to be_closed
      end
    end

    context "when the user can't edit the topic" do
      it "doesn't close the topic" do
        Guardian.any_instance.expects(:can_moderate?).returns(false)
        tba = TopicsBulkAction.new(topic.user, [topic.id], type: "close")
        topic_ids = tba.perform!
        expect(topic_ids).to be_blank
        topic.reload
        expect(topic).not_to be_closed
      end
    end
  end

  describe "archive" do
    context "when the user can moderate the topic" do
      it "archives the topic and returns the topic_id" do
        Guardian.any_instance.expects(:can_moderate?).returns(true)
        Guardian.any_instance.expects(:can_create?).returns(true)
        tba = TopicsBulkAction.new(topic.user, [topic.id], type: "archive")
        topic_ids = tba.perform!
        expect(topic_ids).to eq([topic.id])
        topic.reload
        expect(topic).to be_archived
      end
    end

    context "when the user can't edit the topic" do
      it "doesn't archive the topic" do
        Guardian.any_instance.expects(:can_moderate?).returns(false)
        tba = TopicsBulkAction.new(topic.user, [topic.id], type: "archive")
        topic_ids = tba.perform!
        expect(topic_ids).to be_blank
        topic.reload
        expect(topic).not_to be_archived
      end
    end
  end

  describe "unlist" do
    context "when the user can moderate the topic" do
      it "unlists the topic and returns the topic_id" do
        Guardian.any_instance.expects(:can_moderate?).returns(true)
        Guardian.any_instance.expects(:can_create?).returns(true)
        tba = TopicsBulkAction.new(topic.user, [topic.id], type: "unlist")
        topic_ids = tba.perform!
        expect(topic_ids).to eq([topic.id])
        topic.reload
        expect(topic).not_to be_visible
      end
    end

    context "when the user can't edit the topic" do
      it "doesn't unlist the topic" do
        Guardian.any_instance.expects(:can_moderate?).returns(false)
        tba = TopicsBulkAction.new(topic.user, [topic.id], type: "unlist")
        topic_ids = tba.perform!
        expect(topic_ids).to be_blank
        topic.reload
        expect(topic).to be_visible
      end
    end
  end

  describe "reset_bump_dates" do
    context "when the user can update bumped at" do
      it "does reset the topic bump date" do
        post_created_at = 1.day.ago
        create_post(topic_id: topic.id, created_at: post_created_at)
        topic.update!(bumped_at: 1.hour.ago)
        Guardian.any_instance.expects(:can_update_bumped_at?).returns(true)
        tba = TopicsBulkAction.new(topic.user, [topic.id], type: "reset_bump_dates")
        topic_ids = tba.perform!
        expect(topic_ids).to eq([topic.id])
        expect(topic.reload.bumped_at).to eq_time(post_created_at)
      end
    end

    context "when the user can't update bumped at" do
      it "doesn't reset the topic bump date" do
        create_post(topic_id: topic.id, created_at: 1.day.ago)
        bumped_at = 1.hour.ago
        topic.update!(bumped_at: bumped_at)
        Guardian.any_instance.expects(:can_update_bumped_at?).returns(false)
        tba = TopicsBulkAction.new(topic.user, [topic.id], type: "reset_bump_dates")
        topic_ids = tba.perform!
        expect(topic_ids).to eq([])
        expect(topic.reload.bumped_at).to eq_time(bumped_at)
      end
    end
  end

  describe "change_tags" do
    fab!(:tag1) { Fabricate(:tag) }
    fab!(:tag2) { Fabricate(:tag) }

    before do
      SiteSetting.tagging_enabled = true
      SiteSetting.tag_topic_allowed_groups = Group::AUTO_GROUPS[:trust_level_0]
      topic.tags = [tag1, tag2]
    end

    it "can change the tags, and can create new tags" do
      SiteSetting.create_tag_allowed_groups = Group::AUTO_GROUPS[:trust_level_0]
      tba =
        TopicsBulkAction.new(
          topic.user,
          [topic.id],
          type: "change_tags",
          tags: ["newtag", tag1.name],
        )
      topic_ids = tba.perform!
      expect(topic_ids).to eq([topic.id])
      topic.reload
      expect(topic.tags.map(&:name)).to contain_exactly("newtag", tag1.name)
    end

    it "can change the tags but not create new ones" do
      SiteSetting.create_tag_allowed_groups = Group::AUTO_GROUPS[:trust_level_4]
      tba =
        TopicsBulkAction.new(
          topic.user,
          [topic.id],
          type: "change_tags",
          tags: ["newtag", tag1.name],
        )
      topic_ids = tba.perform!
      expect(topic_ids).to eq([topic.id])
      topic.reload
      expect(topic.tags.map(&:name)).to contain_exactly(tag1.name)
    end

    it "can remove all tags" do
      tba = TopicsBulkAction.new(topic.user, [topic.id], type: "change_tags", tags: [])
      topic_ids = tba.perform!
      expect(topic_ids).to eq([topic.id])
      topic.reload
      expect(topic.tags.size).to eq(0)
    end

    context "when user can't edit topic" do
      before { Guardian.any_instance.expects(:can_edit?).returns(false) }

      it "doesn't change the tags" do
        tba =
          TopicsBulkAction.new(
            topic.user,
            [topic.id],
            type: "change_tags",
            tags: ["newtag", tag1.name],
          )
        topic_ids = tba.perform!
        expect(topic_ids).to eq([])
        topic.reload
        expect(topic.tags.map(&:name)).to contain_exactly(tag1.name, tag2.name)
      end
    end
  end

  describe "append tags" do
    fab!(:tag1) { Fabricate(:tag) }
    fab!(:tag2) { Fabricate(:tag) }
    fab!(:tag3) { Fabricate(:tag) }

    before do
      SiteSetting.tagging_enabled = true
      SiteSetting.tag_topic_allowed_groups = Group::AUTO_GROUPS[:trust_level_0]
      topic.tags = [tag1, tag2]
    end

    it "can append new or existing tags" do
      SiteSetting.create_tag_allowed_groups = Group::AUTO_GROUPS[:trust_level_0]
      tba =
        TopicsBulkAction.new(
          topic.user,
          [topic.id],
          type: "append_tags",
          tags: [tag1.name, tag3.name, "newtag"],
        )
      topic_ids = tba.perform!
      expect(topic_ids).to eq([topic.id])
      topic.reload
      expect(topic.tags.map(&:name)).to contain_exactly(tag1.name, tag2.name, tag3.name, "newtag")
    end

    it "can append empty tags" do
      tba = TopicsBulkAction.new(topic.user, [topic.id], type: "append_tags", tags: [])
      topic_ids = tba.perform!
      expect(topic_ids).to eq([topic.id])
      topic.reload
      expect(topic.tags.map(&:name)).to contain_exactly(tag1.name, tag2.name)
    end

    context "when the user can't create new topics" do
      before { SiteSetting.create_tag_allowed_groups = Group::AUTO_GROUPS[:trust_level_4] }

      it "can append existing tags but doesn't append new tags" do
        tba =
          TopicsBulkAction.new(
            topic.user,
            [topic.id],
            type: "append_tags",
            tags: [tag3.name, "newtag"],
          )
        topic_ids = tba.perform!
        expect(topic_ids).to eq([topic.id])
        topic.reload
        expect(topic.tags.map(&:name)).to contain_exactly(tag1.name, tag2.name, tag3.name)
      end
    end

    context "when user can't edit topic" do
      before { Guardian.any_instance.expects(:can_edit?).returns(false) }

      it "doesn't change the tags" do
        tba =
          TopicsBulkAction.new(
            topic.user,
            [topic.id],
            type: "append_tags",
            tags: ["newtag", tag3.name],
          )
        topic_ids = tba.perform!
        expect(topic_ids).to eq([])
        topic.reload
        expect(topic.tags.map(&:name)).to contain_exactly(tag1.name, tag2.name)
      end
    end
  end

  describe "remove_tags" do
    fab!(:tag1) { Fabricate(:tag) }
    fab!(:tag2) { Fabricate(:tag) }

    before do
      SiteSetting.tagging_enabled = true
      SiteSetting.tag_topic_allowed_groups = Group::AUTO_GROUPS[:trust_level_0]
      topic.tags = [tag1, tag2]
    end

    it "can remove all tags" do
      tba = TopicsBulkAction.new(topic.user, [topic.id], type: "remove_tags")
      topic_ids = tba.perform!
      expect(topic_ids).to eq([topic.id])
      topic.reload
      expect(topic.tags.size).to eq(0)
    end

    context "when user can't edit topic" do
      before { Guardian.any_instance.expects(:can_edit?).returns(false) }

      it "doesn't remove the tags" do
        tba = TopicsBulkAction.new(topic.user, [topic.id], type: "remove_tags")
        topic_ids = tba.perform!
        expect(topic_ids).to eq([])
        topic.reload
        expect(topic.tags.map(&:name)).to contain_exactly(tag1.name, tag2.name)
      end
    end
  end
end