# frozen_string_literal: true

require 'rails_helper'

RSpec.describe User::Confirmation do
  describe 'Scopes' do
    let!(:unconfirmed_user) { Fabricate :user, confirmed_at: nil }
    let!(:confirmed_user) { Fabricate :user, confirmed_at: Time.now.utc }

    describe '.confirmed' do
      it 'returns users who are confirmed' do
        expect(User.confirmed)
          .to contain_exactly(confirmed_user)
      end
    end

    describe '.unconfirmed' do
      it 'returns users who are not confirmed' do
        expect(User.unconfirmed)
          .to contain_exactly(unconfirmed_user)
      end
    end
  end

  describe '#confirmed?' do
    subject { Fabricate.build(:user, confirmed_at:) }

    context 'when confirmed_at is set' do
      let(:confirmed_at) { Time.now.utc }

      it { is_expected.to be_confirmed }
    end

    context 'when confirmed_at is not set' do
      let(:confirmed_at) { nil }

      it { is_expected.to_not be_confirmed }
    end
  end

  describe '#unconfirmed?' do
    subject { Fabricate.build(:user, confirmed_at:) }

    context 'when confirmed_at is set' do
      let(:confirmed_at) { Time.now.utc }

      it { is_expected.to_not be_unconfirmed }
    end

    context 'when confirmed_at is not set' do
      let(:confirmed_at) { nil }

      it { is_expected.to be_unconfirmed }
    end
  end

  describe '#confirm' do
    subject { user.confirm }

    let(:new_email) { 'new-email@host.example' }

    before { allow(TriggerWebhookWorker).to receive(:perform_async) }

    context 'when the user is already confirmed' do
      let!(:user) { Fabricate(:user, confirmed_at: Time.now.utc, approved: true, unconfirmed_email: new_email) }

      it 'sets email to unconfirmed_email and does not trigger web hook' do
        expect { subject }
          .to change { user.reload.email }.to(new_email)
        expect(TriggerWebhookWorker)
          .to_not have_received(:perform_async).with('account.approved', 'Account', user.account_id)
      end
    end

    context 'when the user is a new user' do
      let(:user) { Fabricate(:user, confirmed_at: nil, unconfirmed_email: new_email) }

      context 'when the user does not require explicit approval' do
        before { Setting.registrations_mode = 'open' }

        it 'sets email to unconfirmed_email and triggers `account.approved` web hook' do
          expect { subject }
            .to change { user.reload.email }.to(new_email)
          expect(TriggerWebhookWorker)
            .to have_received(:perform_async).with('account.approved', 'Account', user.account_id).once
        end
      end

      context 'when the user requires explicit approval because of signup IP address' do
        let(:user) { Fabricate(:user, confirmed_at: nil, unconfirmed_email: new_email, approved: false, sign_up_ip: '192.0.2.5') }

        before do
          Setting.registrations_mode = 'open'
          Fabricate(:ip_block, ip: '192.0.2.5', severity: :sign_up_requires_approval)
        end

        it 'sets email to new_email and marks user as confirmed, but not as approved and does not trigger `account.approved` web hook' do
          expect { subject }
            .to change { user.reload.email }.to(new_email)
            .and change { user.reload.confirmed_at }.from(nil)
            .and not_change { user.reload.approved }.from(false)
          expect(TriggerWebhookWorker)
            .to_not have_received(:perform_async).with('account.approved', 'Account', user.account_id)
        end
      end

      context 'when the user requires explicit approval because of username' do
        let(:user) { Fabricate(:user, confirmed_at: nil, unconfirmed_email: new_email, approved: false, account_attributes: { username: 'VeryStrangeUsername' }) }

        before do
          Setting.registrations_mode = 'open'
          Fabricate(:username_block, username: 'StrangeUser', exact: false, allow_with_approval: true)
        end

        it 'sets email to new_email and marks user as confirmed, but not as approved and does not trigger `account.approved` web hook' do
          expect { subject }
            .to change { user.reload.email }.to(new_email)
            .and change { user.reload.confirmed_at }.from(nil)
            .and not_change { user.reload.approved }.from(false)
          expect(TriggerWebhookWorker)
            .to_not have_received(:perform_async).with('account.approved', 'Account', user.account_id)
        end
      end

      context 'when registrations mode is approved' do
        before { Setting.registrations_mode = 'approved' }

        context 'when the user is already approved' do
          before { user.approve! }

          it 'sets email to unconfirmed_email and triggers `account.approved` web hook' do
            expect { subject }
              .to change { user.reload.email }.to(new_email)
            expect(TriggerWebhookWorker)
              .to have_received(:perform_async).with('account.approved', 'Account', user.account_id).once
          end
        end

        context 'when the user is not approved' do
          it 'sets email to unconfirmed_email and does not trigger web hook' do
            expect { subject }
              .to change { user.reload.email }.to(new_email)
            expect(TriggerWebhookWorker)
              .to_not have_received(:perform_async).with('account.approved', 'Account', user.account_id)
          end
        end
      end
    end
  end
end
