require 'spec_helper'

shared_examples 'check total store credit from payments' do
  context 'with valid payments' do
    subject { order }

    let(:order) { payment.order }
    let!(:payment) { create(:store_credit_payment) }
    let!(:second_payment) { create(:store_credit_payment, order: order) }

    it 'returns the sum of the payment amounts' do
      expect(subject.total_applicable_store_credit).to eq (payment.amount + second_payment.amount)
    end
  end

  context 'without valid payments' do
    subject { order }

    let(:order) { create(:order) }

    it 'returns 0' do
      expect(subject.total_applicable_store_credit).to be_zero
    end
  end
end

describe 'Order' do
  describe '#covered_by_store_credit' do
    context "order doesn't have an associated user" do
      subject { create(:store_credits_order_without_user) }

      it 'returns false' do
        expect(subject.covered_by_store_credit).to be false
      end
    end

    context 'order has an associated user' do
      subject { create(:order, user: user) }

      let(:user) { create(:user) }

      context 'user has enough store credit to pay for the order' do
        before do
          allow(user).to receive(:total_available_store_credit).and_return(10.0)
          allow(subject).to receive(:total).and_return(5.0)
        end

        it 'returns true' do
          expect(subject.covered_by_store_credit).to be true
        end
      end

      context 'user does not have enough store credit to pay for the order' do
        before do
          allow(user).to receive(:total_available_store_credit).and_return(0.0)
          allow(subject).to receive(:total).and_return(5.0)
        end

        it 'returns false' do
          expect(subject.covered_by_store_credit).to be false
        end
      end
    end
  end

  describe '#total_available_store_credit' do
    context 'order does not have an associated user' do
      subject { create(:store_credits_order_without_user) }

      it 'returns 0' do
        expect(subject.total_available_store_credit).to be_zero
      end
    end

    context 'order has an associated user' do
      subject { create(:order, user: user) }

      let(:user) { create(:user) }
      let(:available_store_credit) { 25.0 }

      before do
        allow(user).to receive(:total_available_store_credit).and_return(available_store_credit)
      end

      it "returns the user's available store credit" do
        expect(subject.total_available_store_credit).to eq available_store_credit
      end
    end
  end

  describe '#could_use_store_credit?' do
    let!(:store_credit_payment_method) { create(:store_credit_payment_method) }

    context 'order does not have an associated user' do
      subject { create(:store_credits_order_without_user) }

      it { expect(subject.could_use_store_credit?).to be false }
    end

    context 'order has an associated user' do
      subject { create(:order, user: user) }

      let(:user) { create(:user) }

      context 'without store credit' do
        it { expect(subject.could_use_store_credit?).to be false }
      end

      context 'with store credit' do
        let(:available_store_credit) { 25.0 }

        before do
          allow(user).to receive(:total_available_store_credit).and_return(available_store_credit)
        end

        it { expect(subject.could_use_store_credit?).to be true }
      end

      context 'without active Store Credit Payment' do
        let(:available_store_credit) { 25.0 }

        before do
          allow(user).to receive(:total_available_store_credit).and_return(available_store_credit)
          store_credit_payment_method.update_attribute(:active, false)
        end

        it { expect(subject.could_use_store_credit?).to be false }
      end


      context 'without Store Credit Payment' do
        let(:available_store_credit) { 25.0 }

        before do
          allow(user).to receive(:total_available_store_credit).and_return(available_store_credit)
          store_credit_payment_method.destroy
        end

        it { expect(subject.could_use_store_credit?).to be false }
      end
    end
  end

  describe '#order_total_after_store_credit' do
    subject { create(:order, total: order_total) }

    let(:order_total) { 100.0 }

    before do
      allow(subject).to receive(:total_applicable_store_credit).and_return(applicable_store_credit)
    end

    context "order's user has store credits" do
      let(:applicable_store_credit) { 10.0 }

      it 'deducts the applicable store credit' do
        expect(subject.order_total_after_store_credit).to eq (order_total - applicable_store_credit)
      end
    end

    context "order's user does not have any store credits" do
      let(:applicable_store_credit) { 0.0 }

      it 'returns the order total' do
        expect(subject.order_total_after_store_credit).to eq order_total
      end
    end
  end

  describe '#total_applicable_store_credit' do
    context 'order is in the confirm state' do
      before { order.update(state: 'confirm') }

      include_examples 'check total store credit from payments'
    end

    context 'order is completed' do
      before { order.update(state: 'complete') }

      include_examples 'check total store credit from payments'
    end

    context 'order is in any state other than confirm or complete' do
      context 'the associated user has store credits' do
        subject { order }

        let(:store_credit) { create(:store_credit) }
        let(:order) { create(:order, user: store_credit.user) }

        context 'the store credit is more than the order total' do
          let(:order_total) { store_credit.amount - 1 }

          before { order.update(total: order_total) }

          it 'returns the order total' do
            expect(subject.total_applicable_store_credit).to eq order_total
          end
        end

        context 'the store credit is less than the order total' do
          let(:order_total) { store_credit.amount * 10 }

          before { order.update(total: order_total) }

          it 'returns the store credit amount' do
            expect(subject.total_applicable_store_credit).to eq store_credit.amount
          end
        end
      end

      context 'the associated user does not have store credits' do
        subject { order }

        let(:order) { create(:order) }

        it 'returns 0' do
          expect(subject.total_applicable_store_credit).to be_zero
        end
      end

      context 'the order does not have an associated user' do
        subject { create(:store_credits_order_without_user) }

        it 'returns 0' do
          expect(subject.total_applicable_store_credit).to be_zero
        end
      end
    end
  end

  describe '#total_applied_store_credit' do
    context 'with valid payments' do
      subject { order }

      let(:order) { payment.order }
      let!(:payment) { create(:store_credit_payment) }
      let!(:second_payment) { create(:store_credit_payment, order: order) }

      it 'returns the sum of the payment amounts' do
        expect(subject.total_applied_store_credit).to eq (payment.amount + second_payment.amount)
      end
    end

    context 'without valid payments' do
      subject { order }

      let(:order) { create(:order) }

      it 'returns 0' do
        expect(subject.total_applied_store_credit).to be_zero
      end
    end
  end

  describe '#using_store_credit?' do
    subject { create(:order) }

    context 'order has store credit payment' do
      before { allow(subject).to receive(:total_applied_store_credit).and_return(10.0) }

      it { expect(subject.using_store_credit?).to be true }
    end

    context 'order has no store credit payments' do
      before { allow(subject).to receive(:total_applied_store_credit).and_return(0.0) }

      it { expect(subject.using_store_credit?).to be false }
    end
  end

  describe '#display_total_applicable_store_credit' do
    subject { create(:order) }

    let(:total_applicable_store_credit) { 10.00 }

    before do
      allow(subject).to receive(:total_applicable_store_credit).and_return(total_applicable_store_credit)
    end

    it 'returns a money instance' do
      expect(subject.display_total_applicable_store_credit).to be_a(Spree::Money)
    end

    it 'returns a negative amount' do
      expect(subject.display_total_applicable_store_credit.amount_in_cents).to eq (total_applicable_store_credit * -100.0)
    end
  end

  describe '#display_total_applied_store_credit' do
    subject { create(:order) }

    let(:total_applied_store_credit) { 10.00 }

    before do
      allow(subject).to receive(:total_applied_store_credit).and_return(total_applied_store_credit)
    end

    it 'returns a money instance' do
      expect(subject.display_total_applied_store_credit).to be_a(Spree::Money)
    end

    it 'returns a negative amount' do
      expect(subject.display_total_applied_store_credit.amount_in_cents).to eq (total_applied_store_credit * -100.0)
    end
  end

  describe '#display_order_total_after_store_credit' do
    subject { create(:order) }

    let(:order_total_after_store_credit) { 10.00 }

    before do
      allow(subject).to receive(:order_total_after_store_credit).and_return(order_total_after_store_credit)
    end

    it 'returns a money instance' do
      expect(subject.display_order_total_after_store_credit).to be_a(Spree::Money)
    end

    it 'returns the order_total_after_store_credit amount' do
      expect(subject.display_order_total_after_store_credit.amount_in_cents).to eq (order_total_after_store_credit * 100.0)
    end
  end

  describe '#display_total_available_store_credit' do
    subject { create(:order) }

    let(:total_available_store_credit) { 10.00 }

    before do
      allow(subject).to receive(:total_available_store_credit).and_return(total_available_store_credit)
    end

    it 'returns a money instance' do
      expect(subject.display_total_available_store_credit).to be_a(Spree::Money)
    end

    it 'returns the total_available_store_credit amount' do
      expect(subject.display_total_available_store_credit.amount_in_cents).to eq (total_available_store_credit * 100.0)
    end
  end

  describe '#display_store_credit_remaining_after_capture' do
    subject { create(:order) }

    let(:total_available_store_credit)  { 10.00 }
    let(:total_applicable_store_credit) { 5.00 }

    before do
      allow(subject).to receive(:total_available_store_credit).and_return(total_available_store_credit)
      allow(subject).to receive(:total_applicable_store_credit).and_return(total_applicable_store_credit)
    end

    it 'returns a money instance' do
      expect(subject.display_store_credit_remaining_after_capture).to be_a(Spree::Money)
    end

    it "returns all of the user's available store credit minus what's applied to the order amount" do
      amount_remaining = total_available_store_credit - total_applicable_store_credit
      expect(subject.display_store_credit_remaining_after_capture.amount_in_cents).to eq (amount_remaining * 100.0)
    end
  end
end
