# frozen_string_literal: true

require "rails_helper"

RSpec.describe Chat::GuardianExtensions do
  fab!(:user) { Fabricate(:user) }
  fab!(:staff) { Fabricate(:user, admin: true) }
  fab!(:chat_group) { Fabricate(:group) }
  fab!(:channel) { Fabricate(:category_channel) }
  fab!(:dm_channel) { Fabricate(:direct_message_channel) }
  let(:guardian) { Guardian.new(user) }
  let(:staff_guardian) { Guardian.new(staff) }

  it "cannot chat if the user is not in the Chat.allowed_group_ids" do
    SiteSetting.chat_allowed_groups = ""
    expect(guardian.can_chat?).to eq(false)
  end

  it "staff can always chat regardless of chat_allowed_grups" do
    SiteSetting.chat_allowed_groups = ""
    expect(staff_guardian.can_chat?).to eq(true)
  end

  it "allows TL1 to chat by default and by extension higher trust levels" do
    Group.refresh_automatic_groups!
    expect(guardian.can_chat?).to eq(true)
    user.update!(trust_level: TrustLevel[3])
    Group.refresh_automatic_groups!
    expect(guardian.can_chat?).to eq(true)
  end

  it "allows user in specific group to chat" do
    SiteSetting.chat_allowed_groups = chat_group.id
    expect(guardian.can_chat?).to eq(false)
    chat_group.add(user)
    user.reload
    expect(guardian.can_chat?).to eq(true)
  end

  describe "chat channel" do
    it "only staff can create channels" do
      expect(guardian.can_create_chat_channel?).to eq(false)
      expect(staff_guardian.can_create_chat_channel?).to eq(true)
    end

    it "only staff can edit chat channels" do
      expect(guardian.can_edit_chat_channel?).to eq(false)
      expect(staff_guardian.can_edit_chat_channel?).to eq(true)
    end

    it "only staff can close chat channels" do
      channel.update(status: :open)
      expect(guardian.can_change_channel_status?(channel, :closed)).to eq(false)
      expect(staff_guardian.can_change_channel_status?(channel, :closed)).to eq(true)
    end

    it "only staff can open chat channels" do
      channel.update(status: :closed)
      expect(guardian.can_change_channel_status?(channel, :open)).to eq(false)
      expect(staff_guardian.can_change_channel_status?(channel, :open)).to eq(true)
    end

    it "only staff can archive chat channels" do
      channel.update(status: :read_only)
      expect(guardian.can_change_channel_status?(channel, :archived)).to eq(false)
      expect(staff_guardian.can_change_channel_status?(channel, :archived)).to eq(true)
    end

    it "only staff can mark chat channels read_only" do
      channel.update(status: :open)
      expect(guardian.can_change_channel_status?(channel, :read_only)).to eq(false)
      expect(staff_guardian.can_change_channel_status?(channel, :read_only)).to eq(true)
    end

    describe "#can_join_chat_channel?" do
      context "for direct message channels" do
        fab!(:chatable) { Fabricate(:direct_message) }
        fab!(:channel) { Fabricate(:direct_message_channel, chatable: chatable) }

        it "returns false if the user is not part of the direct message" do
          expect(guardian.can_join_chat_channel?(channel)).to eq(false)
        end

        it "returns true if the user is part of the direct message" do
          DirectMessageUser.create!(user: user, direct_message: chatable)
          expect(guardian.can_join_chat_channel?(channel)).to eq(true)
        end
      end

      context "for category channel" do
        fab!(:group) { Fabricate(:group) }
        fab!(:group_user) { Fabricate(:group_user, group: group, user: user) }

        it "returns true if the user can join the category" do
          category =
            Fabricate(
              :private_category,
              group: group,
              permission_type: CategoryGroup.permission_types[:readonly],
            )
          channel.update(chatable: category)
          guardian = Guardian.new(user)
          expect(guardian.can_join_chat_channel?(channel)).to eq(false)

          category =
            Fabricate(
              :private_category,
              group: group,
              permission_type: CategoryGroup.permission_types[:create_post],
            )
          channel.update(chatable: category)
          guardian = Guardian.new(user)
          expect(guardian.can_join_chat_channel?(channel)).to eq(true)

          category =
            Fabricate(
              :private_category,
              group: group,
              permission_type: CategoryGroup.permission_types[:full],
            )
          channel.update(chatable: category)
          guardian = Guardian.new(user)
          expect(guardian.can_join_chat_channel?(channel)).to eq(true)
        end
      end
    end

    describe "#can_flag_in_chat_channel?" do
      alias_matcher :be_able_to_flag_in_chat_channel, :be_can_flag_in_chat_channel

      context "when channel is a direct message channel" do
        let(:channel) { Fabricate(:direct_message_channel) }

        it "returns false" do
          expect(guardian).not_to be_able_to_flag_in_chat_channel(channel)
        end
      end

      context "when channel is a category channel" do
        it "returns true" do
          expect(guardian).to be_able_to_flag_in_chat_channel(channel)
        end
      end

      context "with a private channel" do
        let(:private_group) { Fabricate(:group) }
        let(:private_category) { Fabricate(:private_category, group: private_group) }
        let(:private_channel) { Fabricate(:category_channel, chatable: private_category) }

        context "when the user can't see the channel" do
          it "returns false" do
            expect(guardian).not_to be_able_to_flag_in_chat_channel(private_channel)
          end
        end

        context "when the user can see the channel" do
          before { private_group.add(user) }

          it "returns true" do
            expect(guardian).to be_able_to_flag_in_chat_channel(private_channel)
          end
        end
      end
    end

    describe "#can_flag_chat_message?" do
      let!(:message) { Fabricate(:chat_message, chat_channel: channel) }
      before { SiteSetting.chat_message_flag_allowed_groups = "" }

      context "when user isn't staff" do
        it "returns false" do
          expect(guardian.can_flag_chat_message?(message)).to eq(false)
        end
      end

      context "when user is staff" do
        it "returns true" do
          expect(staff_guardian.can_flag_chat_message?(message)).to eq(true)
        end
      end
    end

    describe "#can_moderate_chat?" do
      context "for category channel" do
        fab!(:category) { Fabricate(:category, read_restricted: true) }

        before { channel.update(chatable: category) }

        it "returns true for staff and false for regular users" do
          expect(staff_guardian.can_moderate_chat?(channel.chatable)).to eq(true)
          expect(guardian.can_moderate_chat?(channel.chatable)).to eq(false)
        end

        context "when enable_category_group_moderation is true" do
          before { SiteSetting.enable_category_group_moderation = true }

          it "returns true if the regular user is part of the reviewable_by_group for the category" do
            moderator = Fabricate(:user)
            mods = Fabricate(:group)
            mods.add(moderator)
            category.update!(reviewable_by_group: mods)
            expect(Guardian.new(Fabricate(:admin)).can_moderate_chat?(channel.chatable)).to eq(true)
            expect(Guardian.new(moderator).can_moderate_chat?(channel.chatable)).to eq(true)
          end
        end
      end

      context "for DM channel" do
        fab!(:dm_channel) { DirectMessage.create! }

        before { channel.update(chatable_type: "DirectMessageType", chatable: dm_channel) }

        it "returns true for staff and false for regular users" do
          expect(staff_guardian.can_moderate_chat?(channel.chatable)).to eq(true)
          expect(guardian.can_moderate_chat?(channel.chatable)).to eq(false)
        end
      end
    end

    describe "#can_restore_chat?" do
      fab!(:message) { Fabricate(:chat_message, chat_channel: channel, user: user) }
      fab!(:chatable) { Fabricate(:category) }

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

        it "disallows a owner to restore" do
          expect(guardian.can_restore_chat?(message, chatable)).to eq(false)
        end

        it "allows a staff to restore" do
          expect(staff_guardian.can_restore_chat?(message, chatable)).to eq(true)
        end
      end

      context "when chatable is a direct message" do
        fab!(:chatable) { DirectMessage.create! }

        it "allows owner to restore" do
          expect(guardian.can_restore_chat?(message, chatable)).to eq(true)
        end

        it "allows staff to restore" do
          expect(staff_guardian.can_restore_chat?(message, chatable)).to eq(true)
        end
      end

      context "when user is not owner of the message" do
        fab!(:message) { Fabricate(:chat_message, chat_channel: channel, user: Fabricate(:user)) }

        context "when chatable is a category" do
          context "when category is not restricted" do
            it "allows staff to restore" do
              expect(staff_guardian.can_restore_chat?(message, chatable)).to eq(true)
            end

            it "disallows any user to restore" do
              expect(guardian.can_restore_chat?(message, chatable)).to eq(false)
            end
          end

          context "when category is restricted" do
            fab!(:chatable) { Fabricate(:category, read_restricted: true) }

            it "allows staff to restore" do
              expect(staff_guardian.can_restore_chat?(message, chatable)).to eq(true)
            end

            it "disallows any user to restore" do
              expect(guardian.can_restore_chat?(message, chatable)).to eq(false)
            end

            context "when group moderation is enabled" do
              before { SiteSetting.enable_category_group_moderation = true }

              it "allows a group moderator to restore" do
                moderator = Fabricate(:user)
                mods = Fabricate(:group)
                mods.add(moderator)
                chatable.update!(reviewable_by_group: mods)
                expect(Guardian.new(moderator).can_restore_chat?(message, chatable)).to eq(true)
              end
            end
          end

          context "when chatable is a direct message" do
            fab!(:chatable) { DirectMessage.create! }

            it "allows staff to restore" do
              expect(staff_guardian.can_restore_chat?(message, chatable)).to eq(true)
            end

            it "disallows any user to restore" do
              expect(guardian.can_restore_chat?(message, chatable)).to eq(false)
            end
          end
        end
      end

      context "when user is owner of the message" do
        context "when chatable is a category" do
          it "allows to restore if owner can see category" do
            expect(guardian.can_restore_chat?(message, chatable)).to eq(true)
          end

          context "when category is restricted" do
            fab!(:chatable) { Fabricate(:category, read_restricted: true) }

            it "disallows to restore if owner can't see category" do
              expect(guardian.can_restore_chat?(message, chatable)).to eq(false)
            end

            it "allows staff to restore" do
              expect(staff_guardian.can_restore_chat?(message, chatable)).to eq(true)
            end
          end
        end

        context "when chatable is a direct message" do
          fab!(:chatable) { DirectMessage.create! }

          it "allows staff to restore" do
            expect(staff_guardian.can_restore_chat?(message, chatable)).to eq(true)
          end

          it "allows owner to restore" do
            expect(guardian.can_restore_chat?(message, chatable)).to eq(true)
          end
        end
      end
    end

    describe "#can_delete_category?" do
      alias_matcher :be_able_to_delete_category, :be_can_delete_category

      let(:category) { channel.chatable }

      context "when user is staff" do
        context "when category has no channel" do
          before do
            category.category_channel.destroy
            category.reload
          end

          it "allows to delete the category" do
            expect(staff_guardian).to be_able_to_delete_category(category)
          end
        end

        context "when category has a channel" do
          context "when channel has no messages" do
            it "allows to delete the category" do
              expect(staff_guardian).to be_able_to_delete_category(category)
            end
          end

          context "when channel has messages" do
            let!(:message) { Fabricate(:chat_message, chat_channel: channel) }

            it "does not allow to delete the category" do
              expect(staff_guardian).not_to be_able_to_delete_category(category)
            end
          end
        end
      end

      context "when user is not staff" do
        context "when category has no channel" do
          before do
            category.category_channel.destroy
            category.reload
          end

          it "does not allow to delete the category" do
            expect(guardian).not_to be_able_to_delete_category(category)
          end
        end

        context "when category has a channel" do
          context "when channel has no messages" do
            it "does not allow to delete the category" do
              expect(guardian).not_to be_able_to_delete_category(category)
            end
          end

          context "when channel has messages" do
            let!(:message) { Fabricate(:chat_message, chat_channel: channel) }

            it "does not allow to delete the category" do
              expect(guardian).not_to be_able_to_delete_category(category)
            end
          end
        end
      end
    end
  end

  describe "#can_create_channel_message?" do
    context "when user is staff" do
      it "returns true if the channel is open" do
        channel.update!(status: :open)
        expect(staff_guardian.can_create_channel_message?(channel)).to eq(true)
      end

      it "returns true if the channel is closed" do
        channel.update!(status: :closed)
        expect(staff_guardian.can_create_channel_message?(channel)).to eq(true)
      end

      it "returns false if the channel is archived" do
        channel.update!(status: :archived)
        expect(staff_guardian.can_create_channel_message?(channel)).to eq(false)
      end

      context "for direct message channels" do
        it "returns true if the channel is open" do
          dm_channel.update!(status: :open)
          expect(staff_guardian.can_create_channel_message?(dm_channel)).to eq(true)
        end
      end
    end

    context "when user is not staff" do
      it "returns true if the channel is open" do
        channel.update!(status: :open)
        expect(guardian.can_create_channel_message?(channel)).to eq(true)
      end

      it "returns false if the channel is closed" do
        channel.update!(status: :closed)
        expect(guardian.can_create_channel_message?(channel)).to eq(false)
      end

      it "returns false if the channel is archived" do
        channel.update!(status: :archived)
        expect(guardian.can_create_channel_message?(channel)).to eq(false)
      end

      context "for direct message channels" do
        before { Group.refresh_automatic_groups! }

        it "it still allows the user to message even if they are not in direct_message_enabled_groups because they are not creating the channel" do
          SiteSetting.direct_message_enabled_groups = Group::AUTO_GROUPS[:trust_level_4]
          dm_channel.update!(status: :open)
          expect(guardian.can_create_channel_message?(dm_channel)).to eq(true)
        end
      end
    end
  end
end
