# frozen_string_literal: true

require "rails_helper"

describe ChatMessageBookmarkable do
  fab!(:user) { Fabricate(:user) }
  fab!(:guardian) { Guardian.new(user) }
  fab!(:other_category) { Fabricate(:private_category, group: Fabricate(:group)) }
  fab!(:category_channel) { Fabricate(:category_channel, chatable: other_category) }
  fab!(:private_category) { Fabricate(:private_category, group: Fabricate(:group)) }
  fab!(:channel) { Fabricate(:category_channel) }

  before do
    Bookmark.register_bookmarkable(ChatMessageBookmarkable)
    UserChatChannelMembership.create(chat_channel: channel, user: user, following: true)
  end

  let!(:message1) { Fabricate(:chat_message, chat_channel: channel) }
  let!(:message2) { Fabricate(:chat_message, chat_channel: channel) }
  let!(:bookmark1) do
    Fabricate(:bookmark, user: user, bookmarkable: message1, name: "something i gotta do")
  end
  let!(:bookmark2) { Fabricate(:bookmark, user: user, bookmarkable: message2) }
  let!(:bookmark3) { Fabricate(:bookmark) }

  subject { RegisteredBookmarkable.new(ChatMessageBookmarkable) }

  describe "#perform_list_query" do
    it "returns all the user's bookmarks" do
      expect(subject.perform_list_query(user, guardian).map(&:id)).to match_array(
        [bookmark1.id, bookmark2.id],
      )
    end

    it "does not return bookmarks for messages inside category chat channels the user cannot access" do
      channel.update(chatable: other_category)
      expect(subject.perform_list_query(user, guardian)).to eq(nil)
      other_category.groups.last.add(user)
      bookmark1.reload
      user.reload
      guardian = Guardian.new(user)
      expect(subject.perform_list_query(user, guardian).map(&:id)).to match_array(
        [bookmark1.id, bookmark2.id],
      )
    end

    it "does not return bookmarks for messages inside direct message chat channels the user cannot access" do
      direct_message = Fabricate(:direct_message)
      channel.update(chatable: direct_message)
      expect(subject.perform_list_query(user, guardian)).to eq(nil)
      DirectMessageUser.create(user: user, direct_message: direct_message)
      bookmark1.reload
      user.reload
      guardian = Guardian.new(user)
      expect(subject.perform_list_query(user, guardian).map(&:id)).to match_array(
        [bookmark1.id, bookmark2.id],
      )
    end

    it "does not return bookmarks for deleted messages" do
      message1.trash!
      guardian = Guardian.new(user)
      expect(subject.perform_list_query(user, guardian).map(&:id)).to match_array([bookmark2.id])
    end
  end

  describe "#perform_search_query" do
    before { SearchIndexer.enable }

    it "returns bookmarks that match by name" do
      ts_query = Search.ts_query(term: "gotta", ts_config: "simple")
      expect(
        subject.perform_search_query(
          subject.perform_list_query(user, guardian),
          "%gotta%",
          ts_query,
        ).map(&:id),
      ).to match_array([bookmark1.id])
    end

    it "returns bookmarks that match by chat message message content" do
      message2.update(message: "some good soup")

      ts_query = Search.ts_query(term: "good soup", ts_config: "simple")
      expect(
        subject.perform_search_query(
          subject.perform_list_query(user, guardian),
          "%good soup%",
          ts_query,
        ).map(&:id),
      ).to match_array([bookmark2.id])

      ts_query = Search.ts_query(term: "blah", ts_config: "simple")
      expect(
        subject.perform_search_query(
          subject.perform_list_query(user, guardian),
          "%blah%",
          ts_query,
        ).map(&:id),
      ).to eq([])
    end
  end

  describe "#can_send_reminder?" do
    it "cannot send the reminder if the message or channel is deleted" do
      expect(subject.can_send_reminder?(bookmark1)).to eq(true)
      bookmark1.bookmarkable.trash!
      bookmark1.reload
      expect(subject.can_send_reminder?(bookmark1)).to eq(false)
      ChatMessage.with_deleted.find_by(id: bookmark1.bookmarkable_id).recover!
      bookmark1.reload
      bookmark1.bookmarkable.chat_channel.trash!
      bookmark1.reload
      expect(subject.can_send_reminder?(bookmark1)).to eq(false)
    end
  end

  describe "#reminder_handler" do
    it "creates a notification for the user with the correct details" do
      expect { subject.send_reminder_notification(bookmark1) }.to change { Notification.count }.by(
        1,
      )
      notification = user.notifications.last
      expect(notification.notification_type).to eq(Notification.types[:bookmark_reminder])
      expect(notification.data).to eq(
        {
          title:
            I18n.t(
              "chat.bookmarkable.notification_title",
              channel_name: bookmark1.bookmarkable.chat_channel.title(bookmark1.user),
            ),
          bookmarkable_url: bookmark1.bookmarkable.url,
          display_username: bookmark1.user.username,
          bookmark_name: bookmark1.name,
          bookmark_id: bookmark1.id,
        }.to_json,
      )
    end
  end

  describe "#can_see?" do
    it "returns false if the chat message is in a channel the user cannot see" do
      expect(subject.can_see?(guardian, bookmark1)).to eq(true)
      bookmark1.bookmarkable.chat_channel.update!(chatable: private_category)
      expect(subject.can_see?(guardian, bookmark1)).to eq(false)
      private_category.groups.last.add(user)
      bookmark1.reload
      user.reload
      guardian = Guardian.new(user)
      expect(subject.can_see?(guardian, bookmark1)).to eq(true)
    end
  end

  describe "#validate_before_create" do
    it "raises InvalidAccess if the user cannot see the chat channel" do
      expect { subject.validate_before_create(guardian, bookmark1.bookmarkable) }.not_to raise_error
      bookmark1.bookmarkable.chat_channel.update!(chatable: private_category)
      expect { subject.validate_before_create(guardian, bookmark1.bookmarkable) }.to raise_error(
        Discourse::InvalidAccess,
      )
      private_category.groups.last.add(user)
      bookmark1.reload
      user.reload
      guardian = Guardian.new(user)
      expect { subject.validate_before_create(guardian, bookmark1.bookmarkable) }.not_to raise_error
    end

    it "raises InvalidAccess if the chat message is deleted" do
      expect { subject.validate_before_create(guardian, bookmark1.bookmarkable) }.not_to raise_error
      bookmark1.bookmarkable.trash!
      bookmark1.reload
      expect { subject.validate_before_create(guardian, bookmark1.bookmarkable) }.to raise_error(
        Discourse::InvalidAccess,
      )
    end
  end

  describe "#cleanup_deleted" do
    it "deletes bookmarks for chat messages deleted more than 3 days ago" do
      bookmark_post = Fabricate(:bookmark, bookmarkable: Fabricate(:post))
      bookmark1.bookmarkable.trash!
      bookmark1.bookmarkable.update!(deleted_at: 4.days.ago)
      subject.cleanup_deleted
      expect(Bookmark.exists?(id: bookmark1.id)).to eq(false)
      expect(Bookmark.exists?(id: bookmark2.id)).to eq(true)
      expect(Bookmark.exists?(id: bookmark_post.id)).to eq(true)
    end
  end
end
