# frozen_string_literal: true

RSpec.describe Chat::ChatChannelMembershipManager do
  fab!(:user) { Fabricate(:user) }
  fab!(:channel1) { Fabricate(:category_channel) }
  fab!(:channel2) { Fabricate(:category_channel) }

  describe ".find_for_user" do
    let!(:membership) do
      Fabricate(:user_chat_channel_membership, user: user, chat_channel: channel1, following: true)
    end

    it "returns nil if it cannot find a membership for the user and channel" do
      expect(described_class.new(channel2).find_for_user(user)).to be_blank
    end

    it "returns the membership for the channel and user" do
      membership = described_class.new(channel1).find_for_user(user)
      expect(membership.chat_channel_id).to eq(channel1.id)
      expect(membership.user_id).to eq(user.id)
      expect(membership.following).to eq(true)
    end

    it "scopes by following and returns nil if it does not match the scope" do
      membership.update!(following: false)
      expect(described_class.new(channel1).find_for_user(user, following: true)).to be_blank
    end
  end

  describe ".follow" do
    it "creates a membership if one does not exist for the user and channel already" do
      membership = nil
      expect { membership = described_class.new(channel1).follow(user) }.to change {
        UserChatChannelMembership.count
      }.by(1)
      expect(membership.following).to eq(true)
      expect(membership.chat_channel).to eq(channel1)
      expect(membership.user).to eq(user)
    end

    it "enqueues user_count recalculation and marks user_count_stale as true" do
      described_class.new(channel1).follow(user)
      expect(channel1.reload.user_count_stale).to eq(true)
      expect_job_enqueued(job: :update_channel_user_count, args: { chat_channel_id: channel1.id })
    end

    it "updates the membership to following if it already existed" do
      membership =
        Fabricate(
          :user_chat_channel_membership,
          user: user,
          chat_channel: channel1,
          following: false,
        )
      expect { membership = described_class.new(channel1).follow(user) }.not_to change {
        UserChatChannelMembership.count
      }
      expect(membership.reload.following).to eq(true)
    end
  end

  describe ".unfollow" do
    it "does nothing if the user is not following the channel" do
      expect(described_class.new(channel2).unfollow(user)).to be_blank
    end

    it "updates following for the membership to false and recalculates the user count" do
      membership =
        Fabricate(
          :user_chat_channel_membership,
          user: user,
          chat_channel: channel1,
          following: true,
        )
      described_class.new(channel1).unfollow(user)
      membership.reload
      expect(membership.following).to eq(false)
      expect(channel1.reload.user_count_stale).to eq(true)
      expect_job_enqueued(job: :update_channel_user_count, args: { chat_channel_id: channel1.id })
    end

    it "does not recalculate user count if the user was already not following the channel" do
      membership =
        Fabricate(
          :user_chat_channel_membership,
          user: user,
          chat_channel: channel1,
          following: false,
        )
      expect_not_enqueued_with(
        job: :update_channel_user_count,
        args: {
          chat_channel_id: channel1.id,
        },
      ) { described_class.new(channel1).unfollow(user) }
      expect(channel1.reload.user_count_stale).to eq(false)
    end
  end
end
