require 'spec_helper'

describe Effective::WebhooksController, type: :controller do
  routes { EffectiveOrders::Engine.routes }

  before { StripeMock.start }
  after { StripeMock.stop }

  let(:order) { FactoryGirl.create(:order) }
  let(:buyer) { Effective::Customer.for_user(order.user) }

  let(:event_hash) { event.to_hash }

  describe '#stripe' do
    let(:event) { StripeMock.mock_webhook_event('customer.subscription.created') }

    it 'retrieves the real event from Stripe based on passed ID' do
      expect(Stripe::Event).to receive(:retrieve) { event_hash[:id] }
      post :stripe, event_hash
      response.code.should eq '200'
    end

    it 'assigns the @event based on the passed ID' do
      post :stripe, event_hash
      assigns(:event).id.should eq event_hash[:id]
      response.code.should eq '200'
    end

    it 'exits immediately when passed a livemode=false event in Production' do
      event_hash[:livemode] = false
      Rails.env.stub(:production?).and_return(true)

      post :stripe, event_hash
      assigns(:event).should eq nil
      response.code.should eq '200'
    end

    it 'exits immediately when passed a non-object event' do
      event_hash[:object] = 'not-object'

      post :stripe, event_hash
      assigns(:event).should eq nil
      response.code.should eq '200'
    end
  end

  describe '#stripe.subscription_created' do
    let(:event) { StripeMock.mock_webhook_event('customer.subscription.created') }

    before { buyer.update_attributes(stripe_customer_id: event.data.object.customer) }

    it 'assigns the existing customer, if exists' do
      post :stripe, event_hash
      assigns(:customer).should eq buyer
    end

    it 'creates a new purchased Order for the Subscription' do
      Effective::Subscription.any_instance.stub(:valid?).and_return(true)
      Effective::Subscription.any_instance.stub(:purchased?).and_return(false)

      post :stripe, event_hash

      assigns(:order).purchased?.should eq true
      assigns(:order).user.should eq buyer.user
      assigns(:order).payment.to_s.include?(event_hash[:id]).should eq true
    end

    it 'does not create an Order for an existing purchased Subscription' do
      Effective::Subscription.any_instance.stub(:valid?).and_return(true)
      Effective::Subscription.any_instance.stub(:purchased?).and_return(true)

      post :stripe, event_hash

      assigns(:order).present?.should eq false
    end
  end

  describe '#stripe.subscription_deleted' do
    let(:event) { StripeMock.mock_webhook_event('customer.subscription.deleted') }
    let!(:subscription) { FactoryGirl.create(:subscription, customer_id: buyer.id) }

    context 'when customer exists' do
      before do
        buyer.update_attributes(stripe_customer_id: event.data.object.customer)
        subscription.stripe_plan_id = event.data.object.plan.id
        subscription.save(validate: false)
      end

      it 'assigns the existing customer' do
        post :stripe, event_hash
        assigns(:customer).should eq buyer
      end

      it 'should destroy customer subscription' do
        expect { post :stripe, event_hash }.to change { buyer.subscriptions.count }.from(1).to(0)
      end

      it 'should invoke subscription_deleted_callback' do
        expect(controller).to receive(:subscription_deleted_callback)
        post :stripe, event_hash
      end
    end

    context 'when customer does not exist' do
      it 'should not destroy any of subscriptions' do
        expect { post :stripe, event_hash }.not_to change { Effective::Subscription.count }
      end

      it 'should not invoke subscription_deleted_callback' do
        expect(controller).not_to receive(:subscription_deleted_callback)
        post :stripe, event_hash
      end
    end
  end

  describe '#stripe.invoice_payment_succeeded' do
    let(:event) { StripeMock.mock_webhook_event('invoice.payment_succeeded') }

    context 'when customer exists' do
      before { buyer.update_attributes(stripe_customer_id: event.data.object.customer) }

      context 'when subscription payments present' do
        context 'with renewals' do
          let(:subscription_mock) { double('subscription', status: 'active', start: 1383672652) }
          let(:subscriptions) { double('subscriptions', retrieve: subscription_mock) }

          before { allow(Stripe::Customer).to receive(:retrieve).and_return(double('customer', subscriptions: subscriptions)) }

          it 'assigns the existing customer, if exists' do
            post :stripe, event_hash
            assigns(:customer).should eq buyer
          end

          it 'should invoke subscription_renewed_callback' do
            controller.should_receive(:subscription_renewed_callback).with(kind_of(Stripe::Event)).once
            post :stripe, event_hash
          end
        end

        context 'without renewals' do
          let(:subscription_mock) { double('subscription', status: 'active', start: 1383759053) }  # start and period.start are equal
          let(:subscriptions) { double('subscriptions', retrieve: subscription_mock) }

          before { Stripe::Customer.should_receive(:retrieve).and_return(double('customer', subscriptions: subscriptions)) }

          it 'should not invoke subscription_renewed_callback' do
            expect(controller).not_to receive(:subscription_renewed_callback)
            post :stripe, event_hash
          end
        end
      end

      context 'when no subscription payments' do
        let(:event) { StripeMock.mock_webhook_event('invoice.payment_succeeded.without_renewals') }

        it 'should not invoke subscription_renewed_callback' do
          expect(controller).not_to receive(:subscription_renewed_callback)
          post :stripe, event_hash
        end
      end
    end

    context 'when customer does not exist' do
      it 'should not invoke subscription_renewed_callback' do
        expect(controller).not_to receive(:subscription_renewed_callback)
        post :stripe, event_hash
      end
    end
  end
end