# frozen_string_literal: true

require "rails_helper"

describe Chat::ChatMessageReactor do
  fab!(:reacting_user) { Fabricate(:user) }
  fab!(:channel) { Fabricate(:category_channel) }
  fab!(:reactor) { described_class.new(reacting_user, channel) }
  fab!(:message_1) { Fabricate(:chat_message, chat_channel: channel, user: reacting_user) }
  let(:subject) { described_class.new(reacting_user, channel) }

  it "calls guardian ensure_can_join_chat_channel!" do
    Guardian.any_instance.expects(:ensure_can_join_chat_channel!).once
    subject.react!(message_id: message_1.id, react_action: :add, emoji: ":+1:")
  end

  it "raises an error if the user cannot see the channel" do
    channel.update!(chatable: Fabricate(:private_category, group: Group[:staff]))
    expect {
      subject.react!(message_id: message_1.id, react_action: :add, emoji: ":+1:")
    }.to raise_error(Discourse::InvalidAccess)
  end

  it "raises an error if the user cannot react" do
    SpamRule::AutoSilence.new(reacting_user).silence_user
    expect {
      subject.react!(message_id: message_1.id, react_action: :add, emoji: ":+1:")
    }.to raise_error(Discourse::InvalidAccess)
  end

  it "raises an error if the channel status is not open" do
    channel.update!(status: ChatChannel.statuses[:archived])
    expect {
      subject.react!(message_id: message_1.id, react_action: :add, emoji: ":+1:")
    }.to raise_error(Discourse::InvalidAccess)
    channel.update!(status: ChatChannel.statuses[:open])
    expect {
      subject.react!(message_id: message_1.id, react_action: :add, emoji: ":+1:")
    }.to change(ChatMessageReaction, :count).by(1)
  end

  it "raises an error if the reaction is not valid" do
    expect {
      reactor.react!(message_id: message_1.id, react_action: :foo, emoji: ":+1:")
    }.to raise_error(Discourse::InvalidParameters)
  end

  it "raises an error if the emoji does not exist" do
    expect {
      reactor.react!(message_id: message_1.id, react_action: :add, emoji: ":woohoo:")
    }.to raise_error(Discourse::InvalidParameters)
  end

  it "raises an error if the message is not found" do
    expect {
      reactor.react!(message_id: -999, react_action: :add, emoji: ":woohoo:")
    }.to raise_error(Discourse::InvalidParameters)
  end

  context "when max reactions has been reached" do
    before do
      emojis = Emoji.all.slice(0, Chat::ChatMessageReactor::MAX_REACTIONS_LIMIT)
      emojis.each do |emoji|
        ChatMessageReaction.create!(
          chat_message: message_1,
          user: reacting_user,
          emoji: ":#{emoji.name}:",
        )
      end
    end

    it "adding a reaction raises an error" do
      expect {
        reactor.react!(
          message_id: message_1.id,
          react_action: :add,
          emoji: ":#{Emoji.all.last.name}:",
        )
      }.to raise_error(Discourse::InvalidAccess)
    end

    it "removing a reaction works" do
      expect {
        reactor.react!(
          message_id: message_1.id,
          react_action: :add,
          emoji: ":#{Emoji.all.first.name}:",
        )
      }.to_not raise_error
    end
  end

  it "creates a membership when not present" do
    expect {
      reactor.react!(message_id: message_1.id, react_action: :add, emoji: ":heart:")
    }.to change(UserChatChannelMembership, :count).by(1)
  end

  it "doesn’t create a membership when present" do
    UserChatChannelMembership.create!(user: reacting_user, chat_channel: channel, following: true)

    expect {
      reactor.react!(message_id: message_1.id, react_action: :add, emoji: ":heart:")
    }.not_to change(UserChatChannelMembership, :count)
  end

  it "can add a reaction" do
    expect {
      reactor.react!(message_id: message_1.id, react_action: :add, emoji: ":heart:")
    }.to change(ChatMessageReaction, :count).by(1)
  end

  it "doesn’t duplicate reactions" do
    ChatMessageReaction.create!(chat_message: message_1, user: reacting_user, emoji: ":heart:")

    expect {
      reactor.react!(message_id: message_1.id, react_action: :add, emoji: ":heart:")
    }.not_to change(ChatMessageReaction, :count)
  end

  it "can remove an existing reaction" do
    ChatMessageReaction.create!(chat_message: message_1, user: reacting_user, emoji: ":heart:")

    expect {
      reactor.react!(message_id: message_1.id, react_action: :remove, emoji: ":heart:")
    }.to change(ChatMessageReaction, :count).by(-1)
  end

  it "does nothing when removing if no reaction found" do
    expect {
      reactor.react!(message_id: message_1.id, react_action: :remove, emoji: ":heart:")
    }.not_to change(ChatMessageReaction, :count)
  end

  it "publishes the reaction" do
    ChatPublisher.expects(:publish_reaction!).once

    reactor.react!(message_id: message_1.id, react_action: :add, emoji: ":heart:")
  end
end
