RSpec::Matchers.define :any_permission_in do |*permissions|
  match { |actual| permissions.include?(actual) }
end

shared_context 'mocked permissions' do
  let (:ldap_name) { "cool_ldap_login" }
  let (:email) { "cool@twitch.tv" }
  let(:admin_account) { BeefCake::User.new(ldap_login: ldap_name, email: email) }

  before do
    session[Authentication::SESSION_LOGIN_NAME_KEY] = ldap_name
    session[Authentication::SESSION_EMAIL_KEY] = email
    session[Authentication::SESSION_2FA_KEY] = true

    allow(BeefCake::User).to receive(:find).with(ldap_name, email: email).and_return(admin_account)
  end
end

shared_context 'ignore permissions' do
  before do
    allow(admin_account).to receive(:permitted?).and_return(true)
    allow(admin_account).to receive(:any_permission?).and_return(true)
  end
end

shared_context 'an authorized user' do
  before do
    if permission.instance_of?(Array)
      permission.each do |p|
        expect(admin_account).to receive(:permitted?).once.with(p).and_return(true)
      end
    else
      expect(admin_account).to receive(:permitted?).with(permission).at_least(:once).and_return(true)
    end
  end
end

shared_context 'an unauthorized user' do
  before do
    if permission.instance_of?(Array)
      permission.each do |p|
        allow(admin_account).to receive(:permitted?).once.with(p).and_return(false)
      end
    else
      expect(admin_account).to receive(:permitted?).with(permission).at_least(:once).and_return(false)
    end
  end
end

shared_context 'an authorized user acting on a twitch user' do
  before do
    if permission.instance_of?(Array)
      permission.each do |p|
        expect(admin_account)
          .to receive(:permitted_on_twitch_user?)
          .with(p, user)
          .at_least(:once)
          .and_return(true)
      end
    else
      expect(admin_account)
        .to receive(:permitted_on_twitch_user?)
        .with(permission, user)
        .at_least(:once)
        .and_return(true)
    end

    if defined?(unauthorized_permission)
      expect(admin_account)
        .to receive(:permitted_on_twitch_user?)
        .with(unauthorized_permission, user)
        .at_least(:once)
        .and_return(false)
    end
  end
end

shared_context 'an unauthorized user acting on a twitch user' do
  before do
    if permission.instance_of?(Array)
      permission.each do |p|
        expect(admin_account)
          .to receive(:permitted_on_twitch_user?)
          .with(p, user)
          .at_least(:once)
          .and_return(false)
      end
    else
      expect(admin_account)
        .to receive(:permitted_on_twitch_user?)
        .with(permission, user)
        .at_least(:once)
        .and_return(false)
    end

    if defined?(unauthorized_permission)
      expect(admin_account)
        .to receive(:permitted_on_twitch_user?)
        .with(unauthorized_permission, user)
        .at_least(:once)
        .and_return(false)
    end
  end
end

shared_examples_for 'it redirects the user to the same page and displays an error' do
  it 'returns a 302, redirects to the same URL and the expected error message' do
    expect(response.code).to eq('302')
    expect(response.location).to eq(response.request.url)
    expect(flash[:error]).to be_present
  end
end

shared_examples_for 'it redirects the user and displays an error' do
  it 'returns a 302, redirects to a different URL and the expected error message' do
    expect(response.code).to eq('302')
    expect(response.location).not_to eq(response.request.url)
    expect(flash[:error]).to be_present
  end
end

shared_examples_for 'it redirects the user and does not display an error' do
  it 'returns a 302 and no error message' do
    expect(response.code).to eq('302')
    expect(flash[:error]).to be_nil
  end
end

shared_examples_for 'it redirects the user and displays a success message' do
  it 'redirects the user with a success message' do
    expect(response.code).to eq('302')
    expect(flash[:error]).to be_nil
    expect(flash[:success]).to be_present
    expect(flash[:warning]).to be_nil
  end
end

shared_examples_for 'it redirects the user and displays a warning message' do
  it 'redirects the user with a warning message' do
    expect(response.code).to eq('302')
    expect(flash[:error]).to be_nil
    expect(flash[:warning]).to be_present
    expect(flash[:success]).to be_nil
  end
end

shared_examples_for 'it returns a success response' do
  it 'returns a 200 status code' do
    expect(response.code).to eq('200')
  end
end

shared_examples_for 'it returns an unauthorized error response' do
  it 'returns a 403 status code' do
    expect(response.code).to eq('403')
  end
end

shared_examples_for 'it returns a success response and displays a danger' do
  it 'returns a 200 and the expected error message' do
    expect(response.code).to eq('200')
    expect(flash[:danger]).to be_present
  end
end

shared_examples_for 'it correctly redacts attributes' do
  it 'nils out unauthorized attributes' do
    unauthorized_attributes.each do |attr|
      expect([attr, item.send(attr)]).to eq([attr, nil])
      expect([attr, item.errors.include?(attr)]).to eq([attr, true])
    end
  end

  it 'does not touch authorized attributes' do
    (item.class.attribute_syms - Set.new(unauthorized_attributes)).each do |attr|
      expect([attr, item.errors.include?(attr)]).to eq([attr, false])
    end
  end
end
