require 'rails_helper'

module Twitch
  RSpec.describe Roles do
    def fake_patch_response(success)
      Faraday::Response.new.tap do |response|
        allow(response)
          .to receive(:success?)
          .and_return(success)
      end
    end

    describe '#update' do
      let(:user) { User.new }

      def test_user(params = {})
        object_double(User.new, { login: 'samus', email: 'samus@twitch.tv', email_verified: true, two_factor_enabled?: true }.merge(params))
      end

      before do
        allow(User).to receive(:find).with('good-user').and_return(test_user)
        allow(User).to receive(:find).with('accepted-admin-domain').and_return(test_user(email: 'samus@justin.tv'))
        allow(User).to receive(:find).with('accepted-mod-domain').and_return(test_user(email: 'samus@mods.justin.tv'))
        allow(User).to receive(:find).with('accepted-external-domain').and_return(test_user(email: 'samus@amazon.com'))
        allow(User).to receive(:find).with('unverified-email').and_return(test_user(email_verified: false))
        allow(User).to receive(:find).with('two-factor-disabled').and_return(test_user(two_factor_enabled?: false))
        allow(User).to receive(:find).with('personal-email').and_return(test_user(email: 'samus@gmail.com'))
        allow(User).to receive(:find).with('removed-from-allowlist-email').and_return(test_user(email: 'samus@amazon.bot'))
      end

      it 'makes a patch request to the users service' do
        allow(Base).to receive(:patch).and_return(fake_patch_response(true))
        Roles.update('good-user', admin: true, subadmin: true, global_mod: false)

        expect(Base).to have_received(:patch)
          .with(
            '/users/good-user/roles',
            body: '{"admin":true,"subadmin":true,"global_mod":false}'
          )
      end

      context "when attempting to remove a role" do
        it "always succeeds when removing all roles" do
          ['accepted-admin-domain', 'accepted-mod-domain', 'accepted-external-domain', 'personal-email', 'removed-from-allowlist-email', 'unverified-email', 'two-factor-disabled'].each do |user_id|
            allow(Base).to receive(:patch).and_return(fake_patch_response(true))
            Roles.update(user_id, admin: false, subadmin: false, global_mod: false)

            expect(Base).to have_received(:patch).with(
              "/users/#{user_id}/roles",
              body: '{"admin":false,"subadmin":false,"global_mod":false}'
            )
          end
        end
      end

      context "when attempting to add a role" do
        it 'succeeds for valid admin user' do
          allow(Base).to receive(:patch).and_return(fake_patch_response(true))
          Roles.update('accepted-admin-domain', admin: true, subadmin: false, global_mod: false)

          expect(Base).to have_received(:patch)
            .with(
              '/users/accepted-admin-domain/roles',
              body: '{"admin":true,"subadmin":false,"global_mod":false}'
            )
        end

        it 'succeeds for valid mod user' do
          allow(Base).to receive(:patch).and_return(fake_patch_response(true))
          Roles.update('accepted-mod-domain', admin: false, subadmin: true, global_mod: true)

          expect(Base).to have_received(:patch)
            .with(
              '/users/accepted-mod-domain/roles',
              body: '{"admin":false,"subadmin":true,"global_mod":true}'
            )
        end

        it 'succeeds for valid external user' do
          allow(Base).to receive(:patch).and_return(fake_patch_response(true))
          Roles.update('accepted-external-domain', admin: false, subadmin: false, global_mod: true)

          expect(Base).to have_received(:patch)
            .with(
              '/users/accepted-external-domain/roles',
              body: '{"admin":false,"subadmin":false,"global_mod":true}'
            )
        end

        it "raises an error if the user's email does not have an accepted domain" do
          expect do
            Roles.update('personal-email', admin: true, subadmin: true, global_mod: false)
          end.to raise_error UpdateRoles::InvalidEmailError
        end

        it "raises an error if setting admin role and the user's email does not have an accepted admin domain" do
          expect do
            Roles.update('accepted-mod-domain', admin: true, subadmin: false, global_mod: false)
          end.to raise_error UpdateRoles::InvalidAdminEmailError
        end

        it "raises an error if setting sub_admin role and the user's email does not have an accepted sub_admin domain" do
          expect do
            Roles.update('accepted-external-domain', admin: false, subadmin: true, global_mod: false)
          end.to raise_error UpdateRoles::InvalidSubAdminEmailError
        end

        it "raises an error if setting global_mod role and the user's email does not have an accepted global_mod domain" do
          allow(Roles).to receive(:acceptable_domain)
            .once
            .with(User.find('removed-from-allowlist-email'))
            .and_return(true)

          expect do
            Roles.update('removed-from-allowlist-email', admin: false, subadmin: false, global_mod: true)
          end.to raise_error UpdateRoles::InvalidGlobalModEmailError
        end

        it "raises an error if the user's email is not verified" do
          expect do
            Roles.update('unverified-email', admin: true, subadmin: true, global_mod: false)
          end.to raise_error UpdateRoles::UnverifiedEmailError
        end

        it 'raises an error if the user does not two factor auth enabled' do
          expect do
            Roles.update('two-factor-disabled', admin: true, subadmin: true, global_mod: false)
          end.to raise_error UpdateRoles::TwoFactorRequiredError
        end
      end

      it 'raises an exception if the request fails' do
        allow(Base).to receive(:patch).and_return(fake_patch_response(false))
        expect { Roles.update('good-user', admin: true, subadmin: true, global_mod: false) }.to raise_error UpdateRoles::UserServiceError
      end
    end
  end
end
