require "rails_helper"

RSpec.describe Follow do
  let(:user) { create(:user) }
  let(:tag) { create(:tag) }
  let(:user_2) { create(:user) }
  let(:suspended_user) { create(:user, :suspended) }
  let(:spam_user) { create(:user, :spam) }

  describe "validations" do
    subject { user.follow(user_2) }

    it { is_expected.to validate_inclusion_of(:subscription_status).in_array(%w[all_articles none]) }
    it { is_expected.to validate_presence_of(:followable_type) }
    it { is_expected.to validate_presence_of(:follower_type) }
    it { is_expected.to validate_presence_of(:subscription_status) }
  end

  it "follows user" do
    user.follow(user_2)
    expect(user.following?(user_2)).to be(true)
  end

  it "calculates points with explicit and implicit combined" do
    user.follow(tag)
    follow = described_class.last
    follow.explicit_points = 2.0
    follow.implicit_points = 3.0
    follow.save
    expect(follow.points).to eq(5.0)
  end

  context "when enqueuing jobs" do
    it "enqueues send notification worker" do
      user.update_column(:badge_achievements_count, 3)
      expect do
        described_class.create(follower: user, followable: user_2)
      end.to change(Follows::SendEmailNotificationWorker.jobs, :size).by(1)
    end

    it "does not enqueue send notification worker if user has no badge achievements" do
      expect do
        described_class.create(follower: user, followable: user_2)
      end.not_to change(Follows::SendEmailNotificationWorker.jobs, :size)
    end
  end

  context "when creating and inline" do
    it "touches the follower user while creating" do
      timestamp = 1.day.ago
      user.update_columns(updated_at: timestamp, last_followed_at: timestamp)
      described_class.create!(follower: user, followable: user_2)

      user.reload
      expect(user.updated_at).to be > timestamp
      expect(user.last_followed_at).to be > timestamp
    end

    it "sends an email notification" do
      allow(ForemInstance).to receive(:smtp_enabled?).and_return(true)
      user.update_column(:badge_achievements_count, 1)
      user_2.notification_setting.update(email_follower_notifications: true)
      expect do
        Sidekiq::Testing.inline! do
          described_class.create!(follower: user, followable: user_2)
        end
      end.to change(EmailMessage, :count).by(1)
    end
  end

  describe "scopes" do
    describe ".non_suspended" do
      before do
        user.follow(user_2)
        user.follow(tag)
        suspended_user.follow(user_2)
        spam_user.follow(user_2)
      end

      it "excludes suspended users from the result", :aggregate_failures do
        result = described_class.non_suspended(user_2.class.name, user_2.id)
        expect(result.map(&:follower)).to include(user)
        expect(result.map(&:follower)).not_to include(suspended_user)
        expect(result.map(&:follower)).not_to include(spam_user)
      end

      it "filters by followable type and id", :aggregate_failures do
        result = described_class.non_suspended("ActsAsTaggableOn::Tag", tag.id)
        expect(result.map(&:follower)).to include(user)
        expect(result.map(&:follower)).not_to include(user_2)
        expect(result.map(&:follower)).not_to include(suspended_user)
      end

      it "includes only Users in the result" do
        result = described_class.non_suspended(user_2.class.name, user_2.id)
        expect(result.map(&:follower).all?(User)).to be(true)
      end

      # Additional test cases for more edge cases
    end
  end
end
