# frozen_string_literal: true

RSpec.describe PostOwnerChanger do
  describe "#change_owner!" do
    fab!(:editor) { Fabricate(:admin) }
    fab!(:user_a) { Fabricate(:user) }
    let(:p1) { create_post(post_number: 1) }
    let(:topic) { p1.topic }
    let(:p2) { create_post(topic: topic, post_number: 2) }
    let(:p3) { create_post }

    it "raises an error with a parameter missing" do
      expect {
        PostOwnerChanger.new(
          post_ids: [p1.id],
          topic_id: topic.id,
          new_owner: nil,
          acting_user: editor,
        )
      }.to raise_error(ArgumentError, /new_owner/)
    end

    it "calls PostRevisor" do
      PostRevisor.any_instance.expects(:revise!)
      PostOwnerChanger.new(
        post_ids: [p1.id],
        topic_id: topic.id,
        new_owner: user_a,
        acting_user: editor,
      ).change_owner!
    end

    it "changes the user" do
      bumped_at = freeze_time topic.bumped_at
      now = Time.zone.now
      freeze_time(now - 1.day)

      old_user = p1.user
      PostActionCreator.like(user_a, p1)
      p1.reload
      expect(p1.topic.like_count).to eq(1)

      freeze_time(now)
      PostOwnerChanger.new(
        post_ids: [p1.id],
        topic_id: topic.id,
        new_owner: user_a,
        acting_user: editor,
      ).change_owner!
      p1.reload
      expect(p1.topic.like_count).to eq(0)
      expect(p1.topic.bumped_at).to eq_time(bumped_at)
      expect(p1.topic.last_post_user_id).to eq(user_a.id)
      expect(old_user).not_to eq(p1.user)
      expect(p1.user).to eq(user_a)
    end

    it "changes multiple posts" do
      PostOwnerChanger.new(
        post_ids: [p1.id, p2.id],
        topic_id: topic.id,
        new_owner: user_a,
        acting_user: editor,
      ).change_owner!
      p1.reload
      p2.reload
      expect(p1.user).not_to eq(nil)
      expect(p1.user).to eq(user_a)
      expect(p1.user).to eq(p2.user)
    end

    it "ignores posts in other topics" do
      PostOwnerChanger.new(
        post_ids: [p1.id, p3.id],
        topic_id: topic.id,
        new_owner: user_a,
        acting_user: editor,
      ).change_owner!
      p1.reload
      p3.reload
      expect(p1.user).to eq(user_a)

      expect(p3.topic_id).not_to eq(p1.topic_id)
      expect(p2.user).not_to eq(user_a)
    end

    it "skips creating new post revision if skip_revision is true" do
      PostOwnerChanger.new(
        post_ids: [p1.id, p2.id],
        topic_id: topic.id,
        new_owner: user_a,
        acting_user: editor,
        skip_revision: true,
      ).change_owner!
      p1.reload
      p2.reload
      expect(p1.revisions.size).to eq(0)
      expect(p2.revisions.size).to eq(0)
    end

    it "changes the user even when the post does not pass validation" do
      p1.update_attribute(:raw, "foo")
      PostOwnerChanger.new(
        post_ids: [p1.id],
        topic_id: topic.id,
        new_owner: user_a,
        acting_user: editor,
      ).change_owner!
      expect(p1.reload.user).to eq(user_a)
    end

    it "changes the user even when the topic does not pass validation" do
      topic.update_column(:title, "short")

      PostOwnerChanger.new(
        post_ids: [p1.id],
        topic_id: topic.id,
        new_owner: user_a,
        acting_user: editor,
      ).change_owner!
      expect(p1.reload.user).to eq(user_a)
    end

    it "changes the owner when the post is deleted" do
      p4 = Fabricate(:post, topic: topic, reply_to_post_number: p2.post_number)
      PostDestroyer.new(editor, p4).destroy

      PostOwnerChanger.new(
        post_ids: [p4.id],
        topic_id: topic.id,
        new_owner: user_a,
        acting_user: editor,
      ).change_owner!
      expect(p4.reload.user).to eq(user_a)
    end

    it "sets 'posted' for TopicUser to true" do
      PostOwnerChanger.new(
        post_ids: [p1.id],
        topic_id: topic.id,
        new_owner: user_a,
        acting_user: editor,
      ).change_owner!
      expect(TopicUser.find_by(topic_id: topic.id, user_id: user_a.id).posted).to eq(true)
    end

    context "when setting topic notification level for the new owner" do
      let(:p4) { create_post(post_number: 2, topic: topic) }

      it "'watching' if the first post gets a new owner" do
        PostOwnerChanger.new(
          post_ids: [p1.id],
          topic_id: topic.id,
          new_owner: user_a,
          acting_user: editor,
        ).change_owner!
        tu = TopicUser.find_by(user_id: user_a.id, topic_id: topic.id)
        expect(tu.notification_level).to eq(3)
      end

      it "'tracking' if other than the first post gets a new owner" do
        PostOwnerChanger.new(
          post_ids: [p4.id],
          topic_id: topic.id,
          new_owner: user_a,
          acting_user: editor,
        ).change_owner!
        tu = TopicUser.find_by(user_id: user_a.id, topic_id: topic.id)
        expect(tu.notification_level).to eq(2)
      end
    end

    context "with integration tests" do
      subject(:change_owners) do
        PostOwnerChanger.new(
          post_ids: [p1.id, p2.id],
          topic_id: topic.id,
          new_owner: user_a,
          acting_user: editor,
        ).change_owner!
      end

      let(:p1user) { p1.user }
      let(:p2user) { p2.user }

      before do
        topic.update!(user_id: p1user.id)

        p1user.user_stat.update!(
          topic_count: 1,
          post_count: 0,
          first_post_created_at: p1.created_at,
        )

        p2user.user_stat.update!(
          topic_count: 0,
          post_count: 1,
          first_post_created_at: p2.created_at,
        )

        UserAction.create!(
          action_type: UserAction::NEW_TOPIC,
          user_id: p1user.id,
          acting_user_id: p1user.id,
          target_post_id: -1,
          target_topic_id: p1.topic_id,
          created_at: p1.created_at,
        )
        UserAction.create!(
          action_type: UserAction::REPLY,
          user_id: p2user.id,
          acting_user_id: p2user.id,
          target_post_id: p2.id,
          target_topic_id: p2.topic_id,
          created_at: p2.created_at,
        )

        UserActionManager.enable
      end

      it "updates users' topic and post counts" do
        PostActionCreator.like(p2user, p1)
        expect(p1user.user_stat.reload.likes_received).to eq(1)

        change_owners

        p1user.reload
        p2user.reload
        user_a.reload
        expect(p1user.topic_count).to eq(0)
        expect(p1user.post_count).to eq(0)
        expect(p2user.topic_count).to eq(0)
        expect(p2user.post_count).to eq(0)
        expect(user_a.topic_count).to eq(1)
        expect(user_a.post_count).to eq(1)

        p1_user_stat = p1user.user_stat

        expect(p1_user_stat.first_post_created_at).to eq(nil)
        expect(p1_user_stat.likes_received).to eq(0)

        p2_user_stat = p2user.user_stat

        expect(p2_user_stat.first_post_created_at).to eq(nil)

        user_a_stat = user_a.user_stat

        expect(user_a_stat.first_post_created_at).to be_present
        expect(user_a_stat.likes_received).to eq(1)
      end

      it "handles whispers" do
        whisper =
          PostCreator.new(
            editor,
            topic_id: p1.topic_id,
            reply_to_post_number: 1,
            post_type: Post.types[:whisper],
            raw: "this is a whispered reply",
          ).create

        user_stat = editor.user_stat

        expect {
          PostOwnerChanger.new(
            post_ids: [whisper.id],
            topic_id: topic.id,
            new_owner: Fabricate(:admin),
            acting_user: editor,
          ).change_owner!
        }.to_not change { user_stat.reload.post_count }
      end

      context "with private message topic" do
        let(:pm) { create_post(archetype: "private_message", target_usernames: [p2user.username]) }
        let(:pm_poster) { pm.user }

        it "should update users' counts" do
          PostActionCreator.like(p2user, pm)

          expect {
            PostOwnerChanger.new(
              post_ids: [pm.id],
              topic_id: pm.topic_id,
              new_owner: user_a,
              acting_user: editor,
            ).change_owner!
          }.to_not change { pm_poster.user_stat.post_count }

          expect(pm_poster.user_stat.likes_received).to eq(0)

          user_a_stat = user_a.user_stat
          expect(user_a_stat.first_post_created_at).to be_present
          expect(user_a_stat.likes_received).to eq(0)
          expect(user_a_stat.post_count).to eq(0)
        end
      end

      it "updates UserAction records" do
        g = Guardian.new(editor)
        expect(UserAction.stats(user_a.id, g)).to eq([])

        change_owners

        expect(UserAction.stats(p1user.id, g)).to eq([])
        expect(UserAction.stats(p2user.id, g)).to eq([])
        stats = UserAction.stats(user_a.id, g)
        expect(stats.size).to eq(2)
        expect(stats[0].action_type).to eq(UserAction::NEW_TOPIC)
        expect(stats[0].count).to eq(1)
        expect(stats[1].action_type).to eq(UserAction::REPLY)
        expect(stats[1].count).to eq(1)
      end

      it "updates reply_to_user_id" do
        p4 =
          Fabricate(
            :post,
            topic: topic,
            reply_to_post_number: p1.post_number,
            reply_to_user_id: p1.user_id,
          )
        p5 =
          Fabricate(
            :post,
            topic: topic,
            reply_to_post_number: p2.post_number,
            reply_to_user_id: p2.user_id,
          )

        PostOwnerChanger.new(
          post_ids: [p1.id],
          topic_id: topic.id,
          new_owner: user_a,
          acting_user: editor,
        ).change_owner!
        p4.reload
        p5.reload

        expect(p4.reply_to_user_id).to eq(user_a.id)
        expect(p5.reply_to_user_id).to eq(p2.user_id)
      end
    end
  end
end