# frozen_string_literal: true

RSpec.describe Jobs::ChatNotifyWatching do
  fab!(:user1) { Fabricate(:user) }
  fab!(:user2) { Fabricate(:user) }
  fab!(:user3) { Fabricate(:user) }
  fab!(:group) { Fabricate(:group) }
  let(:except_user_ids) { [] }

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

  def run_job
    described_class.new.execute(chat_message_id: message.id, except_user_ids: except_user_ids)
  end

  def notification_messages_for(user)
    MessageBus
      .track_publish { run_job }
      .filter { |m| m.channel == "/chat/notification-alert/#{user.id}" }
  end

  context "for a category channel" do
    fab!(:channel) { Fabricate(:category_channel) }
    fab!(:membership1) do
      Fabricate(:user_chat_channel_membership, user: user1, chat_channel: channel)
    end
    fab!(:membership2) do
      Fabricate(:user_chat_channel_membership, user: user2, chat_channel: channel)
    end
    fab!(:membership3) do
      Fabricate(:user_chat_channel_membership, user: user3, chat_channel: channel)
    end
    fab!(:message) do
      Fabricate(:chat_message, chat_channel: channel, user: user1, message: "this is a new message")
    end

    before do
      membership2.update!(
        desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always],
      )
    end

    it "sends a desktop notification" do
      messages = notification_messages_for(user2)

      expect(messages.first.data).to include(
        {
          username: user1.username,
          notification_type: Notification.types[:chat_message],
          post_url: channel.relative_url,
          translated_title:
            I18n.t(
              "discourse_push_notifications.popup.new_chat_message",
              { username: user1.username, channel: channel.title(user2) },
            ),
          tag: Chat::ChatNotifier.push_notification_tag(:message, channel.id),
          excerpt: message.message,
        },
      )
    end

    context "when the channel is muted via membership preferences" do
      before { membership2.update!(muted: true) }

      it "does not send a desktop or mobile notification" do
        PostAlerter.expects(:push_notification).never
        messages = notification_messages_for(user2)
        expect(messages).to be_empty
      end
    end

    context "when mobile_notification_level is always and desktop_notification_level is none" do
      before do
        membership2.update!(
          desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:never],
          mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always],
        )
      end

      it "sends a mobile notification" do
        PostAlerter.expects(:push_notification).with(
          user2,
          has_entries(
            {
              username: user1.username,
              notification_type: Notification.types[:chat_message],
              post_url: channel.relative_url,
              translated_title:
                I18n.t(
                  "discourse_push_notifications.popup.new_chat_message",
                  { username: user1.username, channel: channel.title(user2) },
                ),
              tag: Chat::ChatNotifier.push_notification_tag(:message, channel.id),
              excerpt: message.message,
            },
          ),
        )
        messages = notification_messages_for(user2)
        expect(messages.length).to be_zero
      end

      context "when the channel is muted via membership preferences" do
        before { membership2.update!(muted: true) }

        it "does not send a desktop or mobile notification" do
          PostAlerter.expects(:push_notification).never
          messages = notification_messages_for(user2)
          expect(messages).to be_empty
        end
      end
    end

    context "when the target user cannot chat" do
      before { SiteSetting.chat_allowed_groups = group.id }

      it "does not send a desktop notification" do
        expect(notification_messages_for(user2).count).to be_zero
      end
    end

    context "when the target user cannot see the chat channel" do
      before { channel.update!(chatable: Fabricate(:private_category, group: group)) }

      it "does not send a desktop notification" do
        expect(notification_messages_for(user2).count).to be_zero
      end
    end

    context "when the target user has seen the message already" do
      before { membership2.update!(last_read_message_id: message.id) }

      it "does not send a desktop notification" do
        expect(notification_messages_for(user2).count).to be_zero
      end
    end

    context "when the target user is online via presence channel" do
      before { PresenceChannel.any_instance.expects(:user_ids).returns([user2.id]) }

      it "does not send a desktop notification" do
        expect(notification_messages_for(user2).count).to be_zero
      end
    end

    context "when the target user is suspended" do
      before { user2.update!(suspended_till: 1.year.from_now) }

      it "does not send a desktop notification" do
        expect(notification_messages_for(user2).count).to be_zero
      end
    end

    context "when the target user is inside the except_user_ids array" do
      let(:except_user_ids) { [user2.id] }

      it "does not send a desktop notification" do
        expect(notification_messages_for(user2).count).to be_zero
      end
    end
  end

  context "for a direct message channel" do
    fab!(:channel) do
      Fabricate(:direct_message_channel, users: [user1, user2, user3], with_membership: false)
    end
    fab!(:membership1) do
      Fabricate(:user_chat_channel_membership, user: user1, chat_channel: channel)
    end
    fab!(:membership2) do
      Fabricate(:user_chat_channel_membership, user: user2, chat_channel: channel)
    end
    fab!(:membership3) do
      Fabricate(:user_chat_channel_membership, user: user3, chat_channel: channel)
    end
    fab!(:message) { Fabricate(:chat_message, chat_channel: channel, user: user1) }

    before do
      membership2.update!(
        desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always],
      )
    end

    it "sends a desktop notification" do
      messages = notification_messages_for(user2)

      expect(messages.first.data).to include(
        {
          username: user1.username,
          notification_type: Notification.types[:chat_message],
          post_url: channel.relative_url,
          translated_title:
            I18n.t(
              "discourse_push_notifications.popup.new_direct_chat_message",
              { username: user1.username, channel: channel.title(user2) },
            ),
          tag: Chat::ChatNotifier.push_notification_tag(:message, channel.id),
          excerpt: message.message,
        },
      )
    end

    context "when the channel is muted via membership preferences" do
      before { membership2.update!(muted: true) }

      it "does not send a desktop or mobile notification" do
        PostAlerter.expects(:push_notification).never
        messages = notification_messages_for(user2)
        expect(messages).to be_empty
      end
    end

    context "when mobile_notification_level is always and desktop_notification_level is none" do
      before do
        membership2.update!(
          desktop_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:never],
          mobile_notification_level: UserChatChannelMembership::NOTIFICATION_LEVELS[:always],
        )
      end

      it "sends a mobile notification" do
        PostAlerter.expects(:push_notification).with(
          user2,
          has_entries(
            {
              username: user1.username,
              notification_type: Notification.types[:chat_message],
              post_url: channel.relative_url,
              translated_title:
                I18n.t(
                  "discourse_push_notifications.popup.new_direct_chat_message",
                  { username: user1.username, channel: channel.title(user2) },
                ),
              tag: Chat::ChatNotifier.push_notification_tag(:message, channel.id),
              excerpt: message.message,
            },
          ),
        )
        messages = notification_messages_for(user2)
        expect(messages.length).to be_zero
      end

      context "when the channel is muted via membership preferences" do
        before { membership2.update!(muted: true) }

        it "does not send a desktop or mobile notification" do
          PostAlerter.expects(:push_notification).never
          messages = notification_messages_for(user2)
          expect(messages).to be_empty
        end
      end
    end

    context "when the target user cannot chat" do
      before { SiteSetting.chat_allowed_groups = group.id }

      it "does not send a desktop notification" do
        expect(notification_messages_for(user2).count).to be_zero
      end
    end

    context "when the target user cannot see the chat channel" do
      before { membership2.destroy! }

      it "does not send a desktop notification" do
        expect(notification_messages_for(user2).count).to be_zero
      end
    end

    context "when the target user has seen the message already" do
      before { membership2.update!(last_read_message_id: message.id) }

      it "does not send a desktop notification" do
        expect(notification_messages_for(user2).count).to be_zero
      end
    end

    context "when the target user is online via presence channel" do
      before { PresenceChannel.any_instance.expects(:user_ids).returns([user2.id]) }

      it "does not send a desktop notification" do
        expect(notification_messages_for(user2).count).to be_zero
      end
    end

    context "when the target user is suspended" do
      before { user2.update!(suspended_till: 1.year.from_now) }

      it "does not send a desktop notification" do
        expect(notification_messages_for(user2).count).to be_zero
      end
    end

    context "when the target user is inside the except_user_ids array" do
      let(:except_user_ids) { [user2.id] }

      it "does not send a desktop notification" do
        expect(notification_messages_for(user2).count).to be_zero
      end
    end

    context "when the target user is preventing communication from the message creator" do
      before { UserCommScreener.any_instance.expects(:allowing_actor_communication).returns([]) }

      it "does not send a desktop notification" do
        expect(notification_messages_for(user2).count).to be_zero
      end
    end
  end
end
