# frozen_string_literal: true

require "rails_helper"

describe Chat::ChatMessageCreator do
  fab!(:admin1) { Fabricate(:admin) }
  fab!(:admin2) { Fabricate(:admin) }
  fab!(:user1) { Fabricate(:user, group_ids: [Group::AUTO_GROUPS[:everyone]]) }
  fab!(:user2) { Fabricate(:user) }
  fab!(:user3) { Fabricate(:user) }
  fab!(:user4) { Fabricate(:user) }
  fab!(:admin_group) do
    Fabricate(
      :public_group,
      users: [admin1, admin2],
      mentionable_level: Group::ALIAS_LEVELS[:everyone],
    )
  end
  fab!(:user_group) do
    Fabricate(
      :public_group,
      users: [user1, user2, user3],
      mentionable_level: Group::ALIAS_LEVELS[:everyone],
    )
  end
  fab!(:user_without_memberships) { Fabricate(:user) }
  fab!(:public_chat_channel) { Fabricate(:category_channel) }
  fab!(:dm_chat_channel) do
    Fabricate(
      :direct_message_channel,
      chatable: Fabricate(:direct_message, users: [user1, user2, user3]),
    )
  end
  let(:direct_message_channel) do
    Chat::DirectMessageChannelCreator.create!(acting_user: user1, target_users: [user1, user2])
  end

  before do
    SiteSetting.chat_enabled = true
    SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:everyone]
    SiteSetting.chat_duplicate_message_sensitivity = 0

    # Create channel memberships
    [admin1, admin2, user1, user2, user3].each do |user|
      Fabricate(:user_chat_channel_membership, chat_channel: public_chat_channel, user: user)
    end

    Group.refresh_automatic_groups!
    direct_message_channel
  end

  describe "Integration tests with jobs running immediately" do
    before { Jobs.run_immediately! }

    it "errors when length is less than `chat_minimum_message_length`" do
      SiteSetting.chat_minimum_message_length = 10
      creator =
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "2 short",
        )
      expect(creator.failed?).to eq(true)
      expect(creator.error.message).to match(
        I18n.t(
          "chat.errors.minimum_length_not_met",
          { minimum: SiteSetting.chat_minimum_message_length },
        ),
      )
    end

    it "errors when length is greater than `chat_maximum_message_length`" do
      SiteSetting.chat_maximum_message_length = 100
      creator =
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "a really long and in depth message that is just too detailed" * 100,
        )
      expect(creator.failed?).to eq(true)
      expect(creator.error.message).to match(
        I18n.t(
          "chat.errors.message_too_long",
          { maximum: SiteSetting.chat_maximum_message_length },
        ),
      )
    end

    it "allows message creation when length is less than `chat_minimum_message_length` when upload is present" do
      upload = Fabricate(:upload, user: user1)
      SiteSetting.chat_minimum_message_length = 10
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "2 short",
          upload_ids: [upload.id],
        )
      }.to change { ChatMessage.count }.by(1)
    end

    it "creates messages for users who can see the channel" do
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "this is a message",
        )
      }.to change { ChatMessage.count }.by(1)
    end

    it "updates the channel’s last message date" do
      previous_last_message_sent_at = public_chat_channel.last_message_sent_at

      Chat::ChatMessageCreator.create(
        chat_channel: public_chat_channel,
        user: user1,
        content: "this is a message",
      )

      expect(previous_last_message_sent_at).to be < public_chat_channel.reload.last_message_sent_at
    end

    it "sets the last_editor_id to the user who created the message" do
      message =
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "this is a message",
        ).chat_message
      expect(message.last_editor_id).to eq(user1.id)
    end

    it "publishes a DiscourseEvent for new messages" do
      events =
        DiscourseEvent.track_events do
          Chat::ChatMessageCreator.create(
            chat_channel: public_chat_channel,
            user: user1,
            content: "this is a message",
          )
        end
      expect(events.map { _1[:event_name] }).to include(:chat_message_created)
    end

    it "creates mention notifications for public chat" do
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content:
            "this is a @#{user1.username} message with @system @mentions @#{user2.username} and @#{user3.username}",
        )
        # Only 2 mentions are created because user mentioned themselves, system, and an invalid username.
      }.to change { ChatMention.count }.by(2).and not_change { user1.chat_mentions.count }
    end

    it "mentions are case insensitive" do
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "Hey @#{user2.username.upcase}",
        )
      }.to change { user2.chat_mentions.count }.by(1)
    end

    it "notifies @all properly" do
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "@all",
        )
      }.to change { ChatMention.count }.by(4)

      UserChatChannelMembership.where(user: user2, chat_channel: public_chat_channel).update_all(
        following: false,
      )
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "again! @all",
        )
      }.to change { ChatMention.count }.by(3)
    end

    it "notifies @here properly" do
      admin1.update(last_seen_at: 1.year.ago)
      admin2.update(last_seen_at: 1.year.ago)
      user1.update(last_seen_at: Time.now)
      user2.update(last_seen_at: Time.now)
      user3.update(last_seen_at: Time.now)
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "@here",
        )
      }.to change { ChatMention.count }.by(2)
    end

    it "doesn't sent double notifications when '@here' is mentioned" do
      user2.update(last_seen_at: Time.now)
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "@here @#{user2.username}",
        )
      }.to change { user2.chat_mentions.count }.by(1)
    end

    it "notifies @here plus other mentions" do
      admin1.update(last_seen_at: Time.now)
      admin2.update(last_seen_at: 1.year.ago)
      user1.update(last_seen_at: 1.year.ago)
      user2.update(last_seen_at: 1.year.ago)
      user3.update(last_seen_at: 1.year.ago)
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "@here plus @#{user3.username}",
        )
      }.to change { user3.chat_mentions.count }.by(1)
    end

    it "doesn't create mention notifications for users without a membership record" do
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "hello @#{user_without_memberships.username}",
        )
      }.not_to change { ChatMention.count }
    end

    it "doesn't create mention notifications for users who cannot chat" do
      new_group = Group.create
      SiteSetting.chat_allowed_groups = new_group.id
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "hi @#{user2.username} @#{user3.username}",
        )
      }.not_to change { ChatMention.count }
    end

    it "doesn't create mention notifications for users with chat disabled" do
      user2.user_option.update(chat_enabled: false)
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "hi @#{user2.username}",
        )
      }.not_to change { ChatMention.count }
    end

    it "creates only mention notifications for users with access in private chat" do
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: direct_message_channel,
          user: user1,
          content: "hello there @#{user2.username} and @#{user3.username}",
        )
        # Only user2 should be notified
      }.to change { user2.chat_mentions.count }.by(1).and not_change { user3.chat_mentions.count }
    end

    it "creates a mention notifications for group users that are participating in private chat" do
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: direct_message_channel,
          user: user1,
          content: "hello there @#{user_group.name}",
        )
        # Only user2 should be notified
      }.to change { user2.chat_mentions.count }.by(1).and not_change { user3.chat_mentions.count }
    end

    it "publishes inaccessible mentions when user isn't aren't a part of the channel" do
      ChatPublisher.expects(:publish_inaccessible_mentions).once
      Chat::ChatMessageCreator.create(
        chat_channel: public_chat_channel,
        user: admin1,
        content: "hello @#{user4.username}",
      )
    end

    it "publishes inaccessible mentions when user doesn't have chat access" do
      SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:staff]
      ChatPublisher.expects(:publish_inaccessible_mentions).once
      Chat::ChatMessageCreator.create(
        chat_channel: public_chat_channel,
        user: admin1,
        content: "hello @#{user3.username}",
      )
    end

    it "doesn't publish inaccessible mentions when user is following channel" do
      ChatPublisher.expects(:publish_inaccessible_mentions).never
      Chat::ChatMessageCreator.create(
        chat_channel: public_chat_channel,
        user: admin1,
        content: "hello @#{admin2.username}",
      )
    end

    it "does not create mentions for suspended users" do
      user2.update(suspended_till: Time.now + 10.years)
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: direct_message_channel,
          user: user1,
          content: "hello @#{user2.username}",
        )
      }.not_to change { user2.chat_mentions.count }
    end

    it "does not create @all mentions for users when ignore_channel_wide_mention is enabled" do
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "@all",
        )
      }.to change { ChatMention.count }.by(4)

      user2.user_option.update(ignore_channel_wide_mention: true)
      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "hi! @all",
        )
      }.to change { ChatMention.count }.by(3)
    end

    it "does not create @here mentions for users when ignore_channel_wide_mention is enabled" do
      admin1.update(last_seen_at: 1.year.ago)
      admin2.update(last_seen_at: 1.year.ago)
      user1.update(last_seen_at: Time.now)
      user2.update(last_seen_at: Time.now)
      user2.user_option.update(ignore_channel_wide_mention: true)
      user3.update(last_seen_at: Time.now)

      expect {
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "@here",
        )
      }.to change { ChatMention.count }.by(1)
    end

    describe "replies" do
      fab!(:reply_message) do
        Fabricate(:chat_message, chat_channel: public_chat_channel, user: user2)
      end
      fab!(:unrelated_message_1) { Fabricate(:chat_message, chat_channel: public_chat_channel) }
      fab!(:unrelated_message_2) { Fabricate(:chat_message, chat_channel: public_chat_channel) }

      it "links the message that the user is replying to" do
        message =
          Chat::ChatMessageCreator.create(
            chat_channel: public_chat_channel,
            user: user1,
            content: "this is a message",
            in_reply_to_id: reply_message.id,
          ).chat_message

        expect(message.in_reply_to_id).to eq(reply_message.id)
      end

      it "creates a thread and includes the original message and the reply" do
        message = nil
        expect {
          message =
            Chat::ChatMessageCreator.create(
              chat_channel: public_chat_channel,
              user: user1,
              content: "this is a message",
              in_reply_to_id: reply_message.id,
            ).chat_message
        }.to change { ChatThread.count }.by(1)

        expect(message.reload.thread).not_to eq(nil)
        expect(message.in_reply_to.thread).to eq(message.thread)
        expect(message.thread.original_message).to eq(reply_message)
        expect(message.thread.original_message_user).to eq(reply_message.user)
      end

      context "when the thread_id is provided" do
        fab!(:existing_thread) { Fabricate(:chat_thread, channel: public_chat_channel) }

        it "does not create a thread when one is passed in" do
          message = nil
          expect {
            message =
              Chat::ChatMessageCreator.create(
                chat_channel: public_chat_channel,
                user: user1,
                content: "this is a message",
                thread_id: existing_thread.id,
              ).chat_message
          }.not_to change { ChatThread.count }

          expect(message.reload.thread).to eq(existing_thread)
        end

        it "errors when the thread ID is for a different channel" do
          other_channel_thread = Fabricate(:chat_thread, channel: Fabricate(:chat_channel))
          result =
            Chat::ChatMessageCreator.create(
              chat_channel: public_chat_channel,
              user: user1,
              content: "this is a message",
              thread_id: other_channel_thread.id,
            )
          expect(result.error.message).to eq(I18n.t("chat.errors.thread_invalid_for_channel"))
        end

        it "errors when the thread does not match the in_reply_to thread" do
          reply_message.update!(thread: existing_thread)
          result =
            Chat::ChatMessageCreator.create(
              chat_channel: public_chat_channel,
              user: user1,
              content: "this is a message",
              in_reply_to_id: reply_message.id,
              thread_id: Fabricate(:chat_thread, channel: public_chat_channel).id,
            )
          expect(result.error.message).to eq(I18n.t("chat.errors.thread_does_not_match_parent"))
        end

        it "errors when the root message does not have a thread ID" do
          reply_message.update!(thread: nil)
          result =
            Chat::ChatMessageCreator.create(
              chat_channel: public_chat_channel,
              user: user1,
              content: "this is a message",
              in_reply_to_id: reply_message.id,
              thread_id: existing_thread.id,
            )
          expect(result.error.message).to eq(I18n.t("chat.errors.thread_does_not_match_parent"))
        end
      end

      context "for missing root messages" do
        fab!(:original_message) do
          Fabricate(
            :chat_message,
            chat_channel: public_chat_channel,
            user: user2,
            created_at: 1.day.ago,
          )
        end

        before { reply_message.update!(in_reply_to: original_message) }

        it "raises an error when the root message has been trashed" do
          original_message.trash!
          result =
            Chat::ChatMessageCreator.create(
              chat_channel: public_chat_channel,
              user: user1,
              content: "this is a message",
              in_reply_to_id: reply_message.id,
            )
          expect(result.error.message).to eq(I18n.t("chat.errors.original_message_not_found"))
        end

        it "uses the next message in the chain as the root when the root is deleted" do
          original_message.destroy!
          Chat::ChatMessageCreator.create(
            chat_channel: public_chat_channel,
            user: user1,
            content: "this is a message",
            in_reply_to_id: reply_message.id,
          )
          expect(reply_message.reload.thread).not_to eq(nil)
        end
      end

      context "when there is an existing reply chain" do
        fab!(:old_message_1) do
          Fabricate(
            :chat_message,
            chat_channel: public_chat_channel,
            user: user1,
            created_at: 6.hours.ago,
          )
        end
        fab!(:old_message_2) do
          Fabricate(
            :chat_message,
            chat_channel: public_chat_channel,
            user: user2,
            in_reply_to: old_message_1,
            created_at: 4.hours.ago,
          )
        end
        fab!(:old_message_3) do
          Fabricate(
            :chat_message,
            chat_channel: public_chat_channel,
            user: user1,
            in_reply_to: old_message_2,
            created_at: 1.hour.ago,
          )
        end

        before do
          reply_message.update!(
            created_at: old_message_3.created_at + 1.hour,
            in_reply_to: old_message_3,
          )
        end

        it "creates a thread and updates all the messages in the chain" do
          thread_count = ChatThread.count
          message =
            Chat::ChatMessageCreator.create(
              chat_channel: public_chat_channel,
              user: user1,
              content: "this is a message",
              in_reply_to_id: reply_message.id,
            ).chat_message

          expect(ChatThread.count).to eq(thread_count + 1)
          expect(message.reload.thread).not_to eq(nil)
          expect(message.reload.in_reply_to.thread).to eq(message.thread)
          expect(old_message_1.reload.thread).to eq(message.thread)
          expect(old_message_2.reload.thread).to eq(message.thread)
          expect(old_message_3.reload.thread).to eq(message.thread)
          expect(message.thread.chat_messages.count).to eq(5)
          message =
            Chat::ChatMessageCreator.create(
              chat_channel: public_chat_channel,
              user: user1,
              content: "this is a message",
              in_reply_to_id: reply_message.id,
            ).chat_message
        end

        context "when a thread already exists and the thread_id is passed in" do
          let!(:last_message) do
            Chat::ChatMessageCreator.create(
              chat_channel: public_chat_channel,
              user: user1,
              content: "this is a message",
              in_reply_to_id: reply_message.id,
            ).chat_message
          end
          let!(:existing_thread) { last_message.reload.thread }

          it "does not create a new thread" do
            thread_count = ChatThread.count

            message =
              Chat::ChatMessageCreator.create(
                chat_channel: public_chat_channel,
                user: user1,
                content: "this is a message again",
                in_reply_to_id: last_message.id,
                thread_id: existing_thread.id,
              ).chat_message

            expect(ChatThread.count).to eq(thread_count)
            expect(message.reload.thread).to eq(existing_thread)
            expect(message.reload.in_reply_to.thread).to eq(existing_thread)
            expect(message.thread.chat_messages.count).to eq(6)
          end

          it "errors when the thread does not match the root thread" do
            old_message_1.update!(thread: Fabricate(:chat_thread, channel: public_chat_channel))
            result =
              Chat::ChatMessageCreator.create(
                chat_channel: public_chat_channel,
                user: user1,
                content: "this is a message",
                in_reply_to_id: reply_message.id,
                thread_id: existing_thread.id,
              )
            expect(result.error.message).to eq(I18n.t("chat.errors.thread_does_not_match_parent"))
          end

          it "errors when the root message does not have a thread ID" do
            old_message_1.update!(thread: nil)
            result =
              Chat::ChatMessageCreator.create(
                chat_channel: public_chat_channel,
                user: user1,
                content: "this is a message",
                in_reply_to_id: reply_message.id,
                thread_id: existing_thread.id,
              )
            expect(result.error.message).to eq(I18n.t("chat.errors.thread_does_not_match_parent"))
          end
        end

        context "when there are hundreds of messages in a reply chain already" do
          before do
            previous_message = nil
            1000.times do |i|
              previous_message =
                Fabricate(
                  :chat_message,
                  chat_channel: public_chat_channel,
                  user: [user1, user2].sample,
                  in_reply_to: previous_message,
                  created_at: i.hours.ago,
                )
            end
            @last_message_in_chain = previous_message
          end

          xit "works" do
            thread_count = ChatThread.count

            message = nil
            puts Benchmark.measure {
                   message =
                     Chat::ChatMessageCreator.create(
                       chat_channel: public_chat_channel,
                       user: user1,
                       content: "this is a message",
                       in_reply_to_id: @last_message_in_chain.id,
                     ).chat_message
                 }

            expect(ChatThread.count).to eq(thread_count + 1)
            expect(message.reload.thread).not_to eq(nil)
            expect(message.reload.in_reply_to.thread).to eq(message.thread)
            expect(message.thread.chat_messages.count).to eq(1001)
          end
        end

        context "if the root message alread had a thread" do
          fab!(:old_thread) { Fabricate(:chat_thread, original_message: old_message_1) }
          fab!(:incorrect_thread) { Fabricate(:chat_thread, channel: public_chat_channel) }

          before do
            old_message_1.update!(thread: old_thread)
            old_message_3.update!(thread: incorrect_thread)
          end

          it "does not change any messages in the chain, assumes they have the correct thread ID" do
            thread_count = ChatThread.count
            message =
              Chat::ChatMessageCreator.create(
                chat_channel: public_chat_channel,
                user: user1,
                content: "this is a message",
                in_reply_to_id: reply_message.id,
              ).chat_message

            expect(ChatThread.count).to eq(thread_count)
            expect(message.reload.thread).to eq(old_thread)
            expect(message.reload.in_reply_to.thread).to eq(old_thread)
            expect(old_message_1.reload.thread).to eq(old_thread)
            expect(old_message_2.reload.thread).to eq(old_thread)
            expect(old_message_3.reload.thread).to eq(incorrect_thread)
            expect(message.thread.chat_messages.count).to eq(4)
          end
        end
      end
    end

    describe "group mentions" do
      it "creates chat mentions for group mentions where the group is mentionable" do
        expect {
          Chat::ChatMessageCreator.create(
            chat_channel: public_chat_channel,
            user: user1,
            content: "hello @#{admin_group.name}",
          )
        }.to change { admin1.chat_mentions.count }.by(1).and change {
                admin2.chat_mentions.count
              }.by(1)
      end

      it "doesn't mention users twice if they are direct mentioned and group mentioned" do
        expect {
          Chat::ChatMessageCreator.create(
            chat_channel: public_chat_channel,
            user: user1,
            content: "hello @#{admin_group.name} @#{admin1.username} and @#{admin2.username}",
          )
        }.to change { admin1.chat_mentions.count }.by(1).and change {
                admin2.chat_mentions.count
              }.by(1)
      end

      it "creates chat mentions for group mentions and direct mentions" do
        expect {
          Chat::ChatMessageCreator.create(
            chat_channel: public_chat_channel,
            user: user1,
            content: "hello @#{admin_group.name} @#{user2.username}",
          )
        }.to change { admin1.chat_mentions.count }.by(1).and change {
                admin2.chat_mentions.count
              }.by(1).and change { user2.chat_mentions.count }.by(1)
      end

      it "creates chat mentions for group mentions and direct mentions" do
        expect {
          Chat::ChatMessageCreator.create(
            chat_channel: public_chat_channel,
            user: user1,
            content: "hello @#{admin_group.name} @#{user_group.name}",
          )
        }.to change { admin1.chat_mentions.count }.by(1).and change {
                admin2.chat_mentions.count
              }.by(1).and change { user2.chat_mentions.count }.by(1).and change {
                            user3.chat_mentions.count
                          }.by(1)
      end

      it "doesn't create chat mentions for group mentions where the group is un-mentionable" do
        admin_group.update(mentionable_level: Group::ALIAS_LEVELS[:nobody])
        expect {
          Chat::ChatMessageCreator.create(
            chat_channel: public_chat_channel,
            user: user1,
            content: "hello @#{admin_group.name}",
          )
        }.not_to change { ChatMention.count }
      end
    end

    describe "push notifications" do
      before do
        UserChatChannelMembership.where(user: user1, chat_channel: public_chat_channel).update(
          mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always],
        )
        PresenceChannel.clear_all!
      end

      it "sends a push notification to watching users who are not in chat" do
        PostAlerter.expects(:push_notification).once
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user2,
          content: "Beep boop",
        )
      end

      it "does not send a push notification to watching users who are in chat" do
        PresenceChannel.new("/chat/online").present(user_id: user1.id, client_id: 1)
        PostAlerter.expects(:push_notification).never
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user2,
          content: "Beep boop",
        )
      end
    end

    describe "with uploads" do
      fab!(:upload1) { Fabricate(:upload, user: user1) }
      fab!(:upload2) { Fabricate(:upload, user: user1) }
      fab!(:private_upload) { Fabricate(:upload, user: user2) }

      it "can attach 1 upload to a new message" do
        expect {
          Chat::ChatMessageCreator.create(
            chat_channel: public_chat_channel,
            user: user1,
            content: "Beep boop",
            upload_ids: [upload1.id],
          )
        }.to not_change { chat_upload_count([upload1]) }.and change {
                UploadReference.where(upload_id: upload1.id).count
              }.by(1)
      end

      it "can attach multiple uploads to a new message" do
        expect {
          Chat::ChatMessageCreator.create(
            chat_channel: public_chat_channel,
            user: user1,
            content: "Beep boop",
            upload_ids: [upload1.id, upload2.id],
          )
        }.to not_change { chat_upload_count([upload1, upload2]) }.and change {
                UploadReference.where(upload_id: [upload1.id, upload2.id]).count
              }.by(2)
      end

      it "filters out uploads that weren't uploaded by the user" do
        expect {
          Chat::ChatMessageCreator.create(
            chat_channel: public_chat_channel,
            user: user1,
            content: "Beep boop",
            upload_ids: [private_upload.id],
          )
        }.not_to change { chat_upload_count([private_upload]) }
      end

      it "doesn't attach uploads when `chat_allow_uploads` is false" do
        SiteSetting.chat_allow_uploads = false
        expect {
          Chat::ChatMessageCreator.create(
            chat_channel: public_chat_channel,
            user: user1,
            content: "Beep boop",
            upload_ids: [upload1.id],
          )
        }.to not_change { chat_upload_count([upload1]) }.and not_change {
                UploadReference.where(upload_id: upload1.id).count
              }
      end
    end
  end

  it "destroys draft after message was created" do
    ChatDraft.create!(user: user1, chat_channel: public_chat_channel, data: "{}")

    expect do
      Chat::ChatMessageCreator.create(
        chat_channel: public_chat_channel,
        user: user1,
        content: "Hi @#{user2.username}",
      )
    end.to change { ChatDraft.count }.by(-1)
  end

  describe "watched words" do
    fab!(:watched_word) { Fabricate(:watched_word) }

    it "errors when a blocked word is present" do
      creator =
        Chat::ChatMessageCreator.create(
          chat_channel: public_chat_channel,
          user: user1,
          content: "bad word - #{watched_word.word}",
        )
      expect(creator.failed?).to eq(true)
      expect(creator.error.message).to match(
        I18n.t("contains_blocked_word", { word: watched_word.word }),
      )
    end
  end

  describe "channel statuses" do
    def create_message(user)
      Chat::ChatMessageCreator.create(
        chat_channel: public_chat_channel,
        user: user,
        content: "test message",
      )
    end

    context "when channel is closed" do
      before { public_chat_channel.update(status: :closed) }

      it "errors when trying to create the message for non-staff" do
        creator = create_message(user1)
        expect(creator.failed?).to eq(true)
        expect(creator.error.message).to eq(
          I18n.t(
            "chat.errors.channel_new_message_disallowed",
            status: public_chat_channel.status_name,
          ),
        )
      end

      it "does not error when trying to create a message for staff" do
        expect { create_message(admin1) }.to change { ChatMessage.count }.by(1)
      end
    end

    context "when channel is read_only" do
      before { public_chat_channel.update(status: :read_only) }

      it "errors when trying to create the message for all users" do
        creator = create_message(user1)
        expect(creator.failed?).to eq(true)
        expect(creator.error.message).to eq(
          I18n.t(
            "chat.errors.channel_new_message_disallowed",
            status: public_chat_channel.status_name,
          ),
        )
        creator = create_message(admin1)
        expect(creator.failed?).to eq(true)
        expect(creator.error.message).to eq(
          I18n.t(
            "chat.errors.channel_new_message_disallowed",
            status: public_chat_channel.status_name,
          ),
        )
      end
    end

    context "when channel is archived" do
      before { public_chat_channel.update(status: :archived) }

      it "errors when trying to create the message for all users" do
        creator = create_message(user1)
        expect(creator.failed?).to eq(true)
        expect(creator.error.message).to eq(
          I18n.t(
            "chat.errors.channel_new_message_disallowed",
            status: public_chat_channel.status_name,
          ),
        )
        creator = create_message(admin1)
        expect(creator.failed?).to eq(true)
        expect(creator.error.message).to eq(
          I18n.t(
            "chat.errors.channel_new_message_disallowed",
            status: public_chat_channel.status_name,
          ),
        )
      end
    end
  end

  # TODO (martin) Remove this when we remove ChatUpload completely, 2023-04-01
  def chat_upload_count(uploads)
    DB.query_single(
      "SELECT COUNT(*) FROM chat_uploads WHERE upload_id IN (#{uploads.map(&:id).join(",")})",
    ).first
  end
end
