# frozen_string_literal: true
require 'rails_helper'

RSpec.describe User do
  it do
    is_expected.to have_many(:emails).
      class_name(User::Email.name).
      inverse_of(:user).
      dependent(:destroy)
  end
  it { is_expected.to have_many(:instance_users) }
  it { is_expected.to have_many(:instances).through(:instance_users) }
  it { is_expected.to have_many(:identities).dependent(:destroy) }
  it { is_expected.to have_many(:course_users).dependent(:destroy) }
  it { is_expected.to have_many(:courses).through(:course_users) }

  let!(:instance) { create(:instance) }

  with_tenant(:instance) do
    describe '.system' do
      it 'returns the system user' do
        user = User.system
        expect(user.password).to be_nil
        expect(user.email).to be_nil
        expect(user.built_in?).to be_truthy
      end
    end

    describe '.deleted' do
      it 'returns the deleted user' do
        user = User.deleted
        expect(user.password).to be_nil
        expect(user.email).to be_nil
        expect(user.built_in?).to be_truthy
      end
    end

    describe '#built_in?' do
      context 'when the user is a normal user' do
        it 'returns false' do
          expect(build_stubbed(:user).built_in?).to be_falsey
        end
      end
    end

    describe '#emails' do
      let(:user) { create(:user, emails_count: 5) }
      it 'only allows one primary email' do
        user.emails.each.each { |email_record| email_record.primary = true }
        expect { user.save! } .to raise_error(ActiveRecord::RecordInvalid)
      end
    end

    describe '#role' do
      let(:user) { User.new }
      it 'expects to be normal role by default' do
        expect(user.normal?).to eq(true)
      end
    end

    describe '#destroy' do
      let(:instance) { create(:instance) }
      with_tenant(:instance) do
        let(:user) { create(:user) }
        subject { user.destroy }

        it { is_expected.to be_destroyed }
      end
    end

    describe '.new_with_session' do
      context 'when facebook data is provided' do
        let(:params) { {} }
        let(:facebook_data) { build(:omniauth_facebook) }
        let(:session) { { 'devise.facebook_data' => facebook_data } }
        subject { User.new_with_session(params, session) }

        it 'builds the user with information from facebook' do
          expect(subject.name).to eq(facebook_data.info.name)
          expect(subject.email).to eq(facebook_data.info.email)
        end

        context 'when the user did not authorize access to his email address' do
          let(:facebook_data) { build(:omniauth_facebook, :without_email) }
          let(:email) { generate(:email) }
          let(:params) { { email: email } }

          it 'does not override the value provided in the params' do
            expect(subject.email).to eq(email)
          end
        end
      end
    end

    describe '.search' do
      let(:keyword) { 'KeyWord' }
      let!(:user_with_keyword_in_name) do
        user = create(:user, name: 'Awesome' + keyword + 'User')
        # We should not return multiple instances of same user if it has multiple emails
        create(:user_email, user: user, primary: false)
        user
      end
      let!(:user_with_keyword_in_emails) do
        user = create(:user)
        create(:user_email, user: user, email: keyword + generate(:email), primary: false)
        user
      end

      subject { User.search(keyword.downcase).to_a }
      it 'finds the user' do
        expect(subject.count(user_with_keyword_in_name)).to eq(1)
        expect(subject.count(user_with_keyword_in_emails)).to eq(1)
      end
    end

    context 'after user was created' do
      subject { create(:user) }

      it 'creates an instance_user' do
        expect { subject }.to change(instance.instance_users, :count).by(1)
      end
    end

    describe '.unread' do
      let!(:user) { create(:user) }
      let!(:unread_notification) { create(:user_notification, user: user) }

      it 'returns unread notifications of the user' do
        expect(user.notifications.unread).to include(unread_notification)
      end
    end

    describe '.human_users' do
      subject { User.human_users }

      it 'does not include the system user' do
        expect(subject.find_by(id: User::SYSTEM_USER_ID)).to be_nil
      end
    end

    describe 'validations' do
      context 'when a built in user is specified' do
        let(:built_in_user_stub) do
          stub = build(:user)
          stub.email = nil
          stub.encrypted_password = nil
          allow(stub).to receive(:built_in?).and_return(true)
          stub
        end
        subject { built_in_user_stub }

        it { is_expected.to be_valid }
        it { is_expected.to validate_absence_of(:email) }
        it { is_expected.to validate_absence_of(:encrypted_password) }
        it { is_expected.to validate_absence_of(:authentication_token) }
      end

      describe '#time_zone' do
        subject { build(:user) }

        context 'when time_zone is not set' do
          before { subject.time_zone = nil }

          it { is_expected.to be_valid }
        end

        context 'when time_zone is invalid' do
          before { subject.time_zone = 'LOL' }

          it { is_expected.not_to be_valid }
        end

        context 'when time_zone is valid' do
          before { subject.time_zone = 'Tokyo' }

          it { is_expected.to be_valid }
        end
      end
    end

    describe '#send_reset_password_instructions' do
      subject { create(:user) }

      with_active_job_queue_adapter(:test) do
        it 'sends email with ActiveJob queue' do
          expect { subject.send_reset_password_instructions }.to \
            have_enqueued_job.on_queue('mailers')
        end
      end
    end
  end
end
