require "rails_helper"

RSpec.describe Payments::PurchaseOrdersController do
  include_context "mocked permissions"

  let(:permission) { "purchase_orders" }

  describe "#index" do
    subject { get :index, params: params }

    let(:params) { {} }

    describe "permissions" do
      context "a user with appropriate permissions" do
        include_context 'it creates any tracking call'
        include_context "an authorized user"

        context 'with no params' do
          let(:params) { {} }

          before do
            allow(Payments::PurchaseOrder).to receive(:filter).and_return([])
            subject
          end

          it_behaves_like "it returns a success response"
        end

        context 'with :async param' do
          let(:params) { { async: true } }
          let(:email) { "cool@twitch.tv" }
          it 'should email and display success to user' do
            expect(Payments::EmailPurchaseOrdersReport).to receive(:do).and_return(true)
            subject
            expect(flash[:success]).to eq("Successfully emailed purchase orders CSV to " + email + ".")
          end

          it 'should email and display failure to user' do
            expect(Payments::EmailPurchaseOrdersReport).to receive(:do).and_return(false)
            subject
            expect(flash[:error]).to eq("Failed to email purchase orders to " + email + ".")
          end
        end

        context "with origin_id" do
          let(:params) { { origin_id: origin_id } }
          let(:origin_id) { 'abc123' }
          let(:resp) { [] }

          before do
            allow(Payments::PurchaseOrder).to receive(:filter).and_return(resp)
          end

          it 'should pass the origin to the filter' do
            expect(Payments::PurchaseOrder).to receive(:filter).with(origin_id: origin_id)
            subject
          end

          context 'with no items returned' do
            let(:resp) { [] }
            before { subject }
            it_behaves_like "it redirects the user and displays an error"
          end

          context 'with 1 item returned' do
            let(:resp) { [double(:purchase_order_stub, id: 1, load_associations: [])] }
            before { subject }
            it 'redirects the user' do
              expect(response.code).to eq('302')
            end
          end

          context 'with multiple items returned' do
            let(:resp) { [double(:purchase_order_stub, id: 1, load_associations: []), double(:purchase_order_stub, id: 2, load_associations: [])] }
            before { subject }
            it_behaves_like "it returns a success response"
          end
        end

        context "with purchaser_id" do
          let(:params) { { purchaser_login: purchaser_login } }
          let(:purchaser_login) { 'ninja' }
          let(:user) { Twitch::User.new(id: 12345, login: purchaser_login) }
          let(:resp) { [] }

          before do
            allow(Twitch::User).to receive(:find_by_login).with(purchaser_login).and_return(user)
            allow(Payments::PurchaseOrder).to receive(:filter).and_return(resp)
          end

          context "with no such user" do
            before do
              allow(Twitch::User).to receive(:find_by_login).with(purchaser_login).and_return(nil)
              subject
            end

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

          context "with error on filter" do
            before do
              allow(Payments::PurchaseOrder).to receive(:filter).and_return(nil)
              subject
            end

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

          context 'with no items returned' do
            before { subject }
            it_behaves_like "it returns a success response"
          end

          context 'with items returned' do
            let(:resp) { [double(:purchase_order, load_associations: [])] }
            before { subject }
            it_behaves_like "it returns a success response"
          end
        end

        context "with only recipient_login" do
          let(:params) { { recipient_login: recipient_login } }
          let(:recipient_login) { 'ninja' }
          let(:user) { Twitch::User.new(id: 12345, login: recipient_login) }
          let(:resp) { [] }

          before do
            allow(Twitch::User).to receive(:find_by_login).with(recipient_login).and_return(user)
            allow(Payments::PurchaseOrder).to receive(:filter).and_return(resp)
          end

          context "with no such user" do
            before do
              allow(Twitch::User).to receive(:find_by_login).with(recipient_login).and_return(nil)
              subject
            end

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

          context 'requires more query params' do
            before { subject }
            it_behaves_like "it redirects the user and displays an error"
          end
        end

        context "with only product_short_name" do
          let(:params) { { product_short_name: product_short_name } }
          let(:product_short_name) { 'ninja' }
          let(:products) { [Subscriptions::TicketProduct.new(id: 12345), Subscriptions::TicketProduct.new(id: 12346), Subscriptions::TicketProduct.new(id: 12347)] }
          let(:resp) { [] }

          before do
            allow(Subscriptions::TicketProduct).to receive(:search).with({ short_name: product_short_name, exact_match: true }).and_return(products)
            allow(Payments::PurchaseOrder).to receive(:filter).and_return(resp)
          end

          context "with no such product" do
            before do
              allow(Subscriptions::TicketProduct).to receive(:search).with({ short_name: product_short_name, exact_match: true }).and_return([])
              subject
            end

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

          context 'requires more query params' do
            before { subject }
            it_behaves_like "it redirects the user and displays an error"
          end
        end

        context "with only product_channel_login" do
          let(:params) { { product_channel_login: product_channel_login } }
          let(:product_channel_login) { 'ninja' }
          let(:user) { Twitch::User.new(id: 12345, login: product_channel_login) }
          let(:products) { [Subscriptions::TicketProduct.new(id: 12345), Subscriptions::TicketProduct.new(id: 12346), Subscriptions::TicketProduct.new(id: 12347)] }
          let(:resp) { [] }

          before do
            allow(Twitch::User).to receive(:find_by_login).with(product_channel_login).and_return(user)
            allow(Subscriptions::TicketProduct).to receive(:search).with({ channel_id: user.id, exact_match: true }).and_return(products)
            allow(Payments::PurchaseOrder).to receive(:filter).and_return(resp)
          end

          context "with no such user" do
            before do
              allow(Twitch::User).to receive(:find_by_login).with(product_channel_login).and_return(nil)
              subject
            end

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

          context "with no such product" do
            before do
              allow(Subscriptions::TicketProduct).to receive(:search).with({ channel_id: user.id, exact_match: true }).and_return([])
              subject
            end

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

          context 'requires more query params' do
            before { subject }
            it_behaves_like "it redirects the user and displays an error"
          end
        end

        context "with product_short_name and benefit recipient" do
          let(:params) { { product_short_name: product_short_name, recipient_login: recipient_login } }
          let(:product_short_name) { 'ninja' }
          let(:recipient_login) { 'ninja' }
          let(:user) { Twitch::User.new(id: 12345, login: recipient_login) }
          let(:products) { [Subscriptions::TicketProduct.new(id: 12345), Subscriptions::TicketProduct.new(id: 12346), Subscriptions::TicketProduct.new(id: 12347)] }
          let(:resp) { [] }

          before do
            allow(Subscriptions::TicketProduct).to receive(:search).with({ short_name: product_short_name, exact_match: true }).and_return(products)
            allow(Twitch::User).to receive(:find_by_login).with(recipient_login).and_return(user)
            allow(Payments::PurchaseOrder).to receive(:filter).and_return(resp)
          end

          context 'requires more query params' do
            before { subject }
            it_behaves_like "it redirects the user and displays an error"
          end
        end

        context "with product_type = :gift_sub" do
          let(:params) { { product_type: :gift_sub } }
          let(:resp) { [] }

          before do
            allow(Payments::PurchaseOrder).to receive(:filter).with(hash_including(gift: true, product_type: :sub)).and_return(resp)
            subject
          end

          it_behaves_like "it returns a success response"
        end
      end

      context "a user without appropriate permissions" do
        include_context "an unauthorized user"

        before do
          get :index
        end

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

  describe "#show" do
    let(:sub) do
      double(:sub, blank?: false)
    end
    let(:sub_id) { 54321 }

    let(:purchase_order) do
      purchase_order = Payments::PurchaseOrder.new
      purchase_order.subscription = sub
      purchase_order
    end
    let(:purchase_order_id) { 12345 }

    let(:sub_purchase_orders) do
      [
        purchase_order
      ]
    end

    before do
      allow(sub).to receive(:[]).with("id").and_return(sub_id)
      allow(Payments::PurchaseOrder).to receive(:filter).with(subscription_id: sub_id, per_page: 'all').and_return(sub_purchase_orders)
    end

    describe "permissions" do
      context "a user with appropriate permissions" do
        include_context "an authorized user"

        before do
          allow(Payments::PurchaseOrder).to receive(:find).once
            .with(purchase_order_id.to_s).and_return(purchase_order)

          get :show, params: { id: purchase_order_id }
        end

        it_behaves_like "it returns a success response"

        it "should set @purchase_order and @subscription_purchase_orders" do
          expect(@controller.view_assigns['purchase_order']).to eq(purchase_order)
          expect(@controller.view_assigns['subscription_purchase_orders']).to eq(sub_purchase_orders)
        end
      end

      context "a user without appropriate permissions" do
        include_context "an unauthorized user"

        before do
          get :show, params: { id: purchase_order_id }
        end

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

  describe "#cancel" do
    let(:purchase_order_id) { 12345 }

    describe "permissions" do
      context "a user with appropriate permissions" do
        include_context "an authorized user"
        include_context 'it creates any tracking call'

        before do
          allow(Payments::PurchaseOrder).to receive(:cancel_subscription_for_order).once
            .with(purchase_order_id.to_s).and_return(true)
          post :cancel, params: { purchase_order_id: purchase_order_id }
        end

        it_behaves_like 'it redirects the user and does not display an error'
      end

      context "a user without appropriate permissions" do
        include_context "an unauthorized user"

        before do
          post :cancel, params: { purchase_order_id: purchase_order_id }
        end

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

  describe "#do_not_renew" do
    let(:purchase_order_id) { 12345 }

    describe "permissions" do
      context "a user with appropriate permissions" do
        include_context "an authorized user"
        include_context 'it creates any tracking call'

        before do
          allow(Payments::PurchaseOrder).to receive(:do_not_renew_subscription_for_order).once
            .with(purchase_order_id.to_s).and_return(true)

          post :do_not_renew, params: { purchase_order_id: purchase_order_id }
        end

        it_behaves_like 'it redirects the user and does not display an error'
      end

      context "a user without appropriate permissions" do
        include_context "an unauthorized user"

        before do
          post :do_not_renew, params: { purchase_order_id: purchase_order_id }
        end

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

  describe "#label_fraud" do
    let(:purchase_order_id) { 12345 }

    describe "permissions" do
      context "a user with appropriate permissions" do
        include_context "an authorized user"
        include_context 'it creates any tracking call'

        context "label with fraud" do
          before do
            allow(Payments::PurchaseOrder).to receive(:label_fraud).once
              .with(purchase_order_id.to_s, 'fraud', ldap_name).and_return(true)

            post :label_fraud, params: { purchase_order_id: purchase_order_id, is_fraud: true }
          end

          it_behaves_like 'it redirects the user and displays a success message'
        end
        context "label with not fraud" do
          before do
            allow(Payments::PurchaseOrder).to receive(:label_fraud).once
              .with(purchase_order_id.to_s, 'not_fraud', ldap_name).and_return(true)

            post :label_fraud, params: { purchase_order_id: purchase_order_id, is_fraud: false }
          end

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

      context "a user without appropriate permissions" do
        include_context "an unauthorized user"

        before do
          post :label_fraud, params: { purchase_order_id: purchase_order_id }
        end

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

  describe "#refund_fraud" do
    let(:purchase_order_id) { 12345 }

    describe "permissions" do
      context "a user with appropriate permissions" do
        include_context "an authorized user"
        include_context 'it creates any tracking call'

        before do
          allow(Payments::PurchaseOrder).to receive(:refund_as_fraud).once
            .with(purchase_order_id.to_s, ldap_name).and_return(true)

          post :refund_fraud, params: { purchase_order_id: purchase_order_id }
        end

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

      context "a user without appropriate permissions" do
        include_context "an unauthorized user"

        before do
          post :refund_fraud, params: { purchase_order_id: purchase_order_id }
        end

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

  describe "#mark_refunded_by_finance" do
    let(:purchase_order_id) { 12345 }
    describe "permissions" do
      context "a user with appropriate permissions" do
        include_context "an authorized user"

        before do
          allow(Payments::PurchaseOrder).to receive(:mark_refunded_by_finance).once
            .with(purchase_order_id.to_s, ldap_name).and_return(true)

          post :mark_refunded_by_finance, params: { purchase_order_id: purchase_order_id }
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end
      context "a user without appropriate permissions" do
        include_context "an unauthorized user"

        before do
          post :mark_refunded_by_finance, params: { purchase_order_id: purchase_order_id }
        end

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

  describe "#refund" do
    let(:purchase_order_id) { 12345 }
    let(:refund_qty) {5}

    describe "permissions" do
      context "a user with appropriate permissions" do
        include_context "an authorized user"

        before do
          allow(Payments::PurchaseOrder).to receive(:refund).once
            .with(purchase_order_id.to_s, refund_qty.to_s).and_return(true)

          post :refund, params: { purchase_order_id: purchase_order_id, payments_purchase_order: {:gross_amount => 5, :original_amount => 5, :is_gift => true}}
        end

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

      context "a user without appropriate permissions" do
        include_context "an unauthorized user"

        before do
          post :refund, params: { purchase_order_id: purchase_order_id, payments_purchase_order: {:gross_amount => 5, :original_amount => 5, :is_gift => true}}
        end

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

    describe "refund failure" do
      context "failure to PurchaseOrder.refund" do
        include_context "an authorized user"

        before do
          allow(Payments::PurchaseOrder).to receive(:refund).once
            .with(purchase_order_id.to_s, refund_qty.to_s).and_return(false)

          post :refund, params: { purchase_order_id: purchase_order_id, payments_purchase_order: {:gross_amount => 5, :original_amount => 5, :is_gift => true}}
        end

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