require 'spec_helper'

describe Spree::LegacyUser, type: :model do
  context "#last_incomplete_order" do
    let!(:user) { create(:user) }

    it "excludes orders that are not frontend_viewable" do
      create(:order, user: user, frontend_viewable: false)
      expect(user.last_incomplete_spree_order).to eq nil
    end

    it "can include orders that are not frontend viewable" do
      order = create(:order, user: user, frontend_viewable: false)
      expect(user.last_incomplete_spree_order(only_frontend_viewable: false)).to eq order
    end

    it "can scope to a store" do
      store = create(:store)
      store_1_order = create(:order, user: user, store: store)
      create(:order, user: user, store: create(:store))
      expect(user.last_incomplete_spree_order(store: store)).to eq store_1_order
    end

    it "excludes completed orders" do
      create(:completed_order_with_totals, user: user, created_by: user)
      expect(user.last_incomplete_spree_order).to eq nil
    end

    it "excludes orders created prior to the user's last completed order" do
      create(:order, user: user, created_by: user, created_at: 1.second.ago)
      create(:completed_order_with_totals, user: user, created_by: user)
      expect(user.last_incomplete_spree_order).to eq nil
    end

    context "with completable_order_created_cutoff set" do
      before do
        Spree::Config.completable_order_created_cutoff_days = 1
      end

      it "excludes orders updated outside of the cutoff date" do
        create(:order, user: user, created_by: user, created_at: 3.days.ago, updated_at: 2.days.ago)
        expect(user.last_incomplete_spree_order).to eq nil
      end
    end

    context "with completable_order_created_cutoff set" do
      before do
        Spree::Config.completable_order_updated_cutoff_days = 1
      end

      it "excludes orders updated outside of the cutoff date" do
        create(:order, user: user, created_by: user, created_at: 3.days.ago, updated_at: 2.days.ago)
        expect(user.last_incomplete_spree_order).to eq nil
      end
    end

    it "chooses the most recently created incomplete order" do
      create(:order, user: user, created_at: 1.second.ago)
      order_2 = create(:order, user: user)
      expect(user.last_incomplete_spree_order).to eq order_2
    end

    context "persists order address" do
      let(:bill_address) { create(:address) }
      let(:ship_address) { create(:address) }
      let(:order) { create(:order, user: user, bill_address: bill_address, ship_address: ship_address) }

      it "doesn't create new addresses" do
        user.user_addresses.create(address: bill_address)
        user.user_addresses.create(address: ship_address)
        user.reload

        expect {
          user.persist_order_address(order)
        }.not_to change { Spree::Address.count }
      end

      it "associates both the bill and ship address to the user" do
        user.persist_order_address(order)
        user.save!
        user.user_addresses.reload

        expect(user.user_addresses.find_first_by_address_values(order.bill_address.attributes)).to_not be_nil
        expect(user.user_addresses.find_first_by_address_values(order.ship_address.attributes)).to_not be_nil
      end
    end

    context "payment source" do
      let(:payment_method) { create(:credit_card_payment_method) }
      let!(:cc) do
        create(:credit_card, user_id: user.id, payment_method: payment_method, gateway_customer_profile_id: "2342343")
      end

      it "has payment sources" do
        expect(user.payment_sources.first.gateway_customer_profile_id).not_to be_empty
      end

      it "drops payment source" do
        Spree::Deprecation.silence do
          user.drop_payment_source cc
        end
        expect(cc.gateway_customer_profile_id).to be_nil
      end
    end
  end
end

describe Spree.user_class, type: :model do
  context "reporting" do
    let(:order_value) { BigDecimal.new("80.94") }
    let(:order_count) { 4 }
    let(:orders) { Array.new(order_count, double(total: order_value)) }

    before do
      allow(orders).to receive(:pluck).with(:total).and_return(orders.map(&:total))
      allow(orders).to receive(:count).and_return(orders.length)
    end

    def load_orders
      allow(subject).to receive(:spree_orders).and_return(double(complete: orders))
    end

    describe "#lifetime_value" do
      context "with orders" do
        before { load_orders }
        it "returns the total of completed orders for the user" do
          expect(subject.lifetime_value).to eq(order_count * order_value)
        end
      end
      context "without orders" do
        it "returns 0.00" do
          expect(subject.lifetime_value).to eq BigDecimal("0.00")
        end
      end
    end

    describe "#display_lifetime_value" do
      it "returns a Spree::Money version of lifetime_value" do
        value = BigDecimal("500.05")
        allow(subject).to receive(:lifetime_value).and_return(value)
        expect(subject.display_lifetime_value).to eq Spree::Money.new(value)
      end
    end

    describe "#order_count" do
      before { load_orders }
      it "returns the count of completed orders for the user" do
        expect(subject.order_count).to eq BigDecimal(order_count)
      end
    end

    describe "#average_order_value" do
      context "with orders" do
        before { load_orders }
        it "returns the average completed order price for the user" do
          expect(subject.average_order_value).to eq order_value
        end
      end
      context "without orders" do
        it "returns 0.00" do
          expect(subject.average_order_value).to eq BigDecimal("0.00")
        end
      end
    end

    describe "#display_average_order_value" do
      before { load_orders }
      it "returns a Spree::Money version of average_order_value" do
        value = BigDecimal("500.05")
        allow(subject).to receive(:average_order_value).and_return(value)
        expect(subject.display_average_order_value).to eq Spree::Money.new(value)
      end
    end
  end

  describe "#total_available_store_credit" do
    context "user does not have any associated store credits" do
      subject { create(:user) }

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

    context "user has several associated store credits" do
      let(:user)                     { create(:user) }
      let(:amount)                   { 120.25 }
      let(:additional_amount)        { 55.75 }
      let(:store_credit)             { create(:store_credit, user: user, amount: amount, amount_used: 0.0) }
      let!(:additional_store_credit) { create(:store_credit, user: user, amount: additional_amount, amount_used: 0.0) }

      subject { store_credit.user }

      context "part of the store credit has been used" do
        let(:amount_used) { 35.00 }

        before { store_credit.update_attributes(amount_used: amount_used) }

        context "part of the store credit has been authorized" do
          let(:authorized_amount) { 10 }

          before { additional_store_credit.update_attributes(amount_authorized: authorized_amount) }

          it "returns sum of amounts minus used amount and authorized amount" do
            expect(subject.total_available_store_credit.to_f).to eq(amount + additional_amount - amount_used - authorized_amount)
          end
        end

        context "there are no authorized amounts on any of the store credits" do
          it "returns sum of amounts minus used amount" do
            expect(subject.total_available_store_credit.to_f).to eq(amount + additional_amount - amount_used)
          end
        end
      end

      context "store credits have never been used" do
        context "part of the store credit has been authorized" do
          let(:authorized_amount) { 10 }

          before { additional_store_credit.update_attributes(amount_authorized: authorized_amount) }

          it "returns sum of amounts minus authorized amount" do
            expect(subject.total_available_store_credit.to_f).to eq(amount + additional_amount - authorized_amount)
          end
        end

        context "there are no authorized amounts on any of the store credits" do
          it "returns sum of amounts" do
            expect(subject.total_available_store_credit.to_f).to eq(amount + additional_amount)
          end
        end
      end

      context "all store credits have never been used or authorized" do
        it "returns sum of amounts" do
          expect(subject.total_available_store_credit.to_f).to eq(amount + additional_amount)
        end
      end
    end
  end
end
