require 'rails_helper'

RSpec.describe PayoutEntities::InvitationsController do
  include ServiceHelper

  include_context 'mocked permissions'

  let(:user) { Mocks.twitch_user }

  shared_context 'a user with manage_partner_status permission' do
    before do
      expect(admin_account).to receive(:permitted?).at_least(:once) do |permission|
        [BeefCake::Permission.for(:payout_entities), BeefCake::Permission.for(:manage_partner_status)].include?(permission)
      end
    end
  end

  shared_context 'a user with manage_affiliate_status permission' do
    before do
      expect(admin_account).to receive(:permitted?).at_least(:once) do |arg|
        [BeefCake::Permission.for(:payout_entities), BeefCake::Permission.for(:manage_affiliate_status)].include?(arg)
      end
    end
  end

  shared_context 'a user with manage_affiliate_status and manage_partner_status permissions' do
    before do
      expect(admin_account).to receive(:permitted?).at_least(:once) do |arg|
        [BeefCake::Permission.for(:payout_entities), BeefCake::Permission.for(:manage_affiliate_status), BeefCake::Permission.for(:manage_partner_status)].include?(arg)
      end
    end
  end

  shared_context 'a user without manage_affiliate_status or manage_partner_status permissions' do
    before do
      expect(admin_account).to receive(:permitted?).at_least(:once) do
        false
      end
    end
  end

  shared_context 'a user with manage_developer_status permissions' do
    before do
      expect(admin_account).to receive(:permitted?).at_least(:once) do |arg|
        [BeefCake::Permission.for(:payout_entities), BeefCake::Permission.for(:manage_developer_status)].include?(arg)
      end
    end
  end

  describe 'new' do
    describe 'permissions' do
      let(:action) { :new }

      before do
        expect(Twitch::User).to receive(:find).with(user.id).and_return(user)
      end

      context 'a user with manage_partner_status permission' do
        include_context 'a user with manage_partner_status permission'

        before do
          expect(user).to receive(:payout_type).and_return({})
          get action, params: { user_id: user.id }
        end

        it_behaves_like 'it returns a success response'
      end

      context 'a user with manage_affiliate_status permission' do
        include_context 'a user with manage_affiliate_status permission'

        before do
          expect(user).to receive(:payout_type).and_return({})
          get action, params: { user_id: user.id }
        end

        it_behaves_like 'it returns a success response'
      end

      context 'a user with manage_affiliate_status and manage_partner_status permissions' do
        include_context 'a user with manage_affiliate_status and manage_partner_status permissions'

        before do
          expect(user).to receive(:payout_type).and_return({})
          get action, params: { user_id: user.id }
        end

        it_behaves_like 'it returns a success response'
      end

      context 'a user without manage_affiliate_status or manage_partner_status permissions' do
        include_context 'a user without manage_affiliate_status or manage_partner_status permissions'

        before do
          get action, params: { user_id: user.id }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end
    end

  end

  describe 'create' do
    describe 'permissions' do
      let(:action) { :create }

      before do
        allow_any_instance_of(PayoutEntities::Invitation).to receive(:save).and_return(true)
      end

      context 'a user with manage_partner_status invites to 0cpm category' do
        include_context 'a user with manage_partner_status permission'

        before do
          expect_any_instance_of(PayoutEntities::Invitation).to receive(:save)
          post action, params: { payout_entities_invitation: { user_id: user.id, user_category: PayoutEntities::Invitation::CATEGORY_0CPM } }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      context 'a user with manage_affiliate_status invites to 0cpm category' do
        include_context 'a user with manage_affiliate_status permission'

        before do
          expect_any_instance_of(PayoutEntities::Invitation).not_to receive(:save)
          post action, params: { payout_entities_invitation: { user_id: user.id, user_category: PayoutEntities::Invitation::CATEGORY_0CPM } }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end

      context 'a user with manage_partner_status and manage_affiliate_status invites to 0cpm category' do
        include_context 'a user with manage_affiliate_status and manage_partner_status permissions'

        before do
          expect_any_instance_of(PayoutEntities::Invitation).to receive(:save)
          post action, params: { payout_entities_invitation: { user_id: user.id, user_category: PayoutEntities::Invitation::CATEGORY_0CPM } }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      context 'a user with manage_partner_status invites to partner category' do
        include_context 'a user with manage_partner_status permission'

        before do
          expect_any_instance_of(PayoutEntities::Invitation).to receive(:save)
          post action, params: { payout_entities_invitation: { user_id: user.id, user_category: PayoutEntities::Invitation::CATEGORY_PARTNER } }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      context 'a user with manage_affiliate_status invites to partner category' do
        include_context 'a user with manage_affiliate_status permission'

        before do
          expect_any_instance_of(PayoutEntities::Invitation).not_to receive(:save)
          post action, params: { payout_entities_invitation: { user_id: user.id, user_category: PayoutEntities::Invitation::CATEGORY_PARTNER } }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end

      context 'a user with manage_partner_status and manage_affiliate_status invites to partner category' do
        include_context 'a user with manage_affiliate_status and manage_partner_status permissions'

        before do
          expect_any_instance_of(PayoutEntities::Invitation).to receive(:save)
          post action, params: { payout_entities_invitation: { user_id: user.id, user_category: PayoutEntities::Invitation::CATEGORY_PARTNER } }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      context 'a user with manage_partner_status invites to affiliate category' do
        include_context 'a user with manage_partner_status permission'

        before do
          expect_any_instance_of(PayoutEntities::Invitation).not_to receive(:save)
          post action, params: { payout_entities_invitation: { user_id: user.id, user_category: PayoutEntities::Invitation::CATEGORY_AFFILIATE } }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end

      context 'a user with manage_affiliate_status invites to affiliate category' do
        include_context 'a user with manage_affiliate_status permission'

        before do
          expect_any_instance_of(PayoutEntities::Invitation).to receive(:save)
          post action, params: { payout_entities_invitation: { user_id: user.id, user_category: PayoutEntities::Invitation::CATEGORY_AFFILIATE } }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      context 'a user with manage_partner_status and manage_affiliate_status invites to affiliate category' do
        include_context 'a user with manage_affiliate_status and manage_partner_status permissions'

        before do
          expect_any_instance_of(PayoutEntities::Invitation).to receive(:save)
          post action, params: { payout_entities_invitation: { user_id: user.id, user_category: PayoutEntities::Invitation::CATEGORY_AFFILIATE } }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end
    end
  end

  describe 'destroy' do
    describe 'permissions' do
      let(:action) { :destroy }

      before do
        expect(Twitch::User).to receive(:find).with(user.id).and_return(user)
        allow(PayoutEntities::Invitation).to receive(:destroy).and_return(true)
      end

      context 'a user with manage_partner_status revokes a partner invitation' do
        include_context 'a user with manage_partner_status permission'

        before do
          expect(PayoutEntities::Invitation).to receive(:destroy)
          delete action, params: { user_id: user.id, id: 'bar', category: PayoutEntities::Invitation::CATEGORY_PARTNER }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      context 'a user with manage_affiliate_status revokes a partner invitation' do
        include_context 'a user with manage_affiliate_status permission'

        before do
          expect(PayoutEntities::Invitation).not_to receive(:destroy)
          post action, params: { user_id: user.id, id: 'bar', category: PayoutEntities::Invitation::CATEGORY_PARTNER }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end

      context 'a user with manage_partner_status and manage_affiliate_status revokes a partner invitation' do
        include_context 'a user with manage_affiliate_status and manage_partner_status permissions'

        before do
          expect(PayoutEntities::Invitation).to receive(:destroy)
          post action, params: { user_id: user.id, id: 'bar', category: PayoutEntities::Invitation::CATEGORY_PARTNER }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      context 'a user with manage_partner_status revokes an affiliate invitation' do
        include_context 'a user with manage_partner_status permission'

        before do
          expect(PayoutEntities::Invitation).not_to receive(:destroy)
          post action, params: { user_id: user.id, id: 'bar', category: PayoutEntities::Invitation::CATEGORY_AFFILIATE }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end

      context 'a user with manage_affiliate_status revokes an affiliate invitation' do
        include_context 'a user with manage_affiliate_status permission'

        before do
          expect(PayoutEntities::Invitation).to receive(:destroy)
          post action, params: { user_id: user.id, id: 'bar', category: PayoutEntities::Invitation::CATEGORY_AFFILIATE }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      context 'a user with manage_partner_status and manage_affiliate_status revokes an affiliate invitation' do
        include_context 'a user with manage_affiliate_status and manage_partner_status permissions'

        before do
          expect(PayoutEntities::Invitation).to receive(:destroy)
          post action, params: { user_id: user.id, id: 'bar', category: PayoutEntities::Invitation::CATEGORY_AFFILIATE }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      context 'a user with manage_partner_status and manage_affiliate_status revokes an invitation with an unknown type' do
        include_context 'a user with manage_affiliate_status and manage_partner_status permissions'

        before do
          expect(PayoutEntities::Invitation).to_not receive(:destroy)
          post action, params: { user_id: user.id, id: 'bar', category: "FAKECATEGORY" }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end

      context 'a user with manage_developer_status revokes a developer invitation' do
        include_context 'a user with manage_developer_status permissions'

        before do
          expect(PayoutEntities::Invitation).to receive(:destroy)
          post action, params: { user_id: user.id, id: 'bar', category: PayoutEntities::Invitation::CATEGORY_DEVELOPER }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end
    end
  end

  # because why make things easy
  def make_payout_type(category)
    ActiveSupport::HashWithIndifferentAccess.new({ category: category })
  end

  describe 'remove' do
    describe 'permissions' do
      let(:action) { :remove }

      before do
        expect(Twitch::User).to receive(:find).with(user.id).and_return(user)
        allow(PayoutEntities::Invitation).to receive(:remove).and_return(fake_delete_response(true))
      end

      # remove from 0cpm
      context 'a user with manage_partner_status removes a user from the 0cpm program' do
        include_context 'a user with manage_partner_status permission'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_0CPM))
          expect(PayoutEntities::Invitation).to receive(:remove)
          post action, params: { user_id: user.id, commit: '' }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      context 'a user with manage_affiliate_status removes a user from the 0cpm program' do
        include_context 'a user with manage_affiliate_status permission'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_0CPM))
          expect(PayoutEntities::Invitation).not_to receive(:remove)
          post action, params: { user_id: user.id, commit: '' }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end

      context 'a user with manage_partner_status and manage_affiliate_status removes a user from the 0cpm program' do
        include_context 'a user with manage_affiliate_status and manage_partner_status permissions'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_0CPM))
          expect(PayoutEntities::Invitation).to receive(:remove)
          post action, params: { user_id: user.id, commit: '' }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      # remove from partner program
      context 'a user with manage_partner_status removes a user from the partner program' do
        include_context 'a user with manage_partner_status permission'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_PARTNER))
          expect(PayoutEntities::Invitation).to receive(:remove)
          post action, params: { user_id: user.id, commit: '' }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      context 'a user with manage_affiliate_status removes a user from the partner program' do
        include_context 'a user with manage_affiliate_status permission'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_PARTNER))
          expect(PayoutEntities::Invitation).not_to receive(:remove)
          post action, params: { user_id: user.id, commit: '' }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end

      context 'a user with manage_partner_status and manage_affiliate_status removes a user from the partner program' do
        include_context 'a user with manage_affiliate_status and manage_partner_status permissions'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_PARTNER))
          expect(PayoutEntities::Invitation).to receive(:remove)
          post action, params: { user_id: user.id, commit: '' }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      # remove from affiliate program
      context 'a user with manage_partner_status removes a user from the affiliate program' do
        include_context 'a user with manage_partner_status permission'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_AFFILIATE))
          expect(PayoutEntities::Invitation).not_to receive(:remove)
          post action, params: { user_id: user.id, commit: '' }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end

      context 'a user with manage_affiliate_status removes a user from the affiliate program' do
        include_context 'a user with manage_affiliate_status permission'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_AFFILIATE))
          expect(PayoutEntities::Invitation).to receive(:remove)
          post action, params: { user_id: user.id, commit: '' }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      context 'a user with manage_partner_status and manage_affiliate_status removes a user from the affiliate program' do
        include_context 'a user with manage_affiliate_status and manage_partner_status permissions'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_AFFILIATE))
          expect(PayoutEntities::Invitation).to receive(:remove)
          post action, params: { user_id: user.id, commit: '' }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      # downgrade 0cpm to affiliate
      context 'a user with manage_partner_status downgrades a 0cpm partner to an affiliate' do
        include_context 'a user with manage_partner_status permission'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_0CPM))
          expect(PayoutEntities::Invitation).not_to receive(:remove)
          post action, params: { user_id: user.id, commit: PayoutEntities::Invitation::ACTION_DOWNGRADE }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end

      context 'a user with manage_affiliate_status downgrades a 0cpm partner to an affiliate' do
        include_context 'a user with manage_affiliate_status permission'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_0CPM))
          expect(PayoutEntities::Invitation).not_to receive(:remove)
          post action, params: { user_id: user.id, commit: PayoutEntities::Invitation::ACTION_DOWNGRADE }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end

      context 'a user with manage_partner_status and manage_affiliate_status downgrades a 0cpm partner to an affiliate' do
        include_context 'a user with manage_affiliate_status and manage_partner_status permissions'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_0CPM))
          expect(PayoutEntities::Invitation).to receive(:remove)
          post action, params: { user_id: user.id, commit: PayoutEntities::Invitation::ACTION_DOWNGRADE }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      # downgrade partner to affiliate
      context 'a user with manage_partner_status downgrades a full partner to an affiliate' do
        include_context 'a user with manage_partner_status permission'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_PARTNER))
          expect(PayoutEntities::Invitation).not_to receive(:remove)
          post action, params: { user_id: user.id, commit: PayoutEntities::Invitation::ACTION_DOWNGRADE }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end

      context 'a user with manage_affiliate_status downgrades a full partner to an affiliate' do
        include_context 'a user with manage_affiliate_status permission'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_PARTNER))
          expect(PayoutEntities::Invitation).not_to receive(:remove)
          post action, params: { user_id: user.id, commit: PayoutEntities::Invitation::ACTION_DOWNGRADE }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end

      context 'a user with manage_partner_status and manage_affiliate_status downgrades a full partner to an affiliate' do
        include_context 'a user with manage_affiliate_status and manage_partner_status permissions'

        before do
          expect_any_instance_of(Twitch::User).to receive(:payout_type).and_return(make_payout_type(PayoutEntities::Invitation::CATEGORY_PARTNER))
          expect(PayoutEntities::Invitation).to receive(:remove)
          post action, params: { user_id: user.id, commit: PayoutEntities::Invitation::ACTION_DOWNGRADE }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end
    end
  end
end
