require 'spec_helper'

describe Subscription do
  fixtures :users, :subscriptions, :subscription_plans, :credit_cards


  before(:each) do
    Freemium.configuration.gateway = Freemium::Gateways::Test.new
  end

  it "should find billable" do
    # making a one-off fixture set, basically
    create_billable_subscription # this subscription should be billable
    create_billable_subscription(:paid_through => Date.today) # this subscription should be billable
    create_billable_subscription(:coupon => Coupon.create!(:description => "Complimentary", :discount_percentage => 100)) # should NOT be billable because it's free
    create_billable_subscription(:subscription_plan => subscription_plans(:free)) # should NOT be billable because it's free
    create_billable_subscription(:paid_through => Date.today + 1) # should NOT be billable because it's paid far enough out
    s = create_billable_subscription # should be billable because it's past due
    s.update_attribute :expire_on, Date.today + 1

    expirable = Subscription.send(:find_billable)
    expirable.all? { |subscription| subscription.paid?                      }.should be_true, "free subscriptions aren't billable"
    expirable.all? { |subscription| !subscription.in_trial?                 }.should be_true, "subscriptions that have been paid are no longer in the trial period"
    expirable.all? { |subscription| subscription.paid_through <= Date.today }.should be_true, "subscriptions paid through tomorrow aren't billable yet"
    expirable.size.should eql(3)

    Freemium.configuration.gateway.stub!(:charge).and_return(
      Transaction.new(
        :billing_key => s.billing_key,
        :amount => s.rate,
        :success => false
      )
    )
  end

  it "should not change expire_on on failure overdue payment" do
    subscription = create_billable_subscription # should NOT be billable because it's already expiring
    expire_on = Date.today + 2
    paid_through = subscription.paid_through
    subscription.update_attribute :expire_on, expire_on
    subscription.reload
    subscription.expire_on.should eql(expire_on)
    expirable = Subscription.send(:find_billable)
    expirable.size.should eql(1), "Subscriptions in their grace period should be retried"

    Freemium.configuration.gateway.stub!(:charge).and_return(
      Transaction.new(
        :billing_key => subscription.billing_key,
        :amount => subscription.rate,
        :success => false
      )
    )

    lambda do
      transaction = subscription.charge!
      subscription.expire_on.should eql(expire_on), "Billing failed on existing overdue account but the expire_on date was changed"
    end.should_not raise_error
  end

  it "should change expire_on on success overdue payment" do
    subscription = create_billable_subscription # should NOT be billable because it's already expiring
    expire_on = Date.today + 2
    paid_through = subscription.paid_through
    subscription.update_attribute :expire_on, expire_on

    expirable = Subscription.send(:find_billable)
    expirable.size.should eql(1), "Subscriptions in their grace period should be retried"

    Freemium.configuration.gateway.stub!(:charge).and_return(
      Transaction.new(
        :billing_key => subscription.billing_key,
        :amount => subscription.rate,
        :success => true
      )
    )

    assert_nothing_raised do
      transaction = subscription.charge!
      transaction.subscription.paid_through.to_s.should eql((paid_through >> 1).to_s), "extended by a month"
      subscription.expire_on.should be_nil, "Billing succeeded on existing overdue account but the expire_on date was not reset"
    end
  end

  it "should charge a subscription" do
    subscription = Subscription.first
    subscription.coupon = Coupon.create!(:description => "Complimentary", :discount_percentage => 30)
    subscription.save!

    paid_through = subscription.paid_through

    Freemium.configuration.gateway.stub!(:charge).and_return(
      Transaction.new(
        :billing_key => subscription.billing_key,
        :amount => subscription.rate,
        :success => true
      )
    )

    lambda do
      transaction = subscription.charge!
      transaction.subscription.paid_through.to_s.should eql((paid_through >> 1).to_s)#, "extended by a month"
    end.should_not raise_error

    subscription = subscription.reload
    subscription.transactions.empty?.should be_false
    subscription.transactions.last.should be_true
    subscription.transactions.last.success?.should be_true
    subscription.transactions.last.message?.should_not be_nil
    ((Time.now - 1.minute) < subscription.last_transaction_at).should be_true
    Transaction.since(Date.today).empty?.should be_false
    subscription.transactions.last.amount.should eql(subscription.rate)
    subscription.reload.paid_through.to_s.should eql((paid_through >> 1).to_s), "extended by a month"
  end


  it "should charge an aborted subscription" do
    subscription = Subscription.first
    subscription.coupon = Coupon.create!(:description => "Complimentary", :discount_percentage => 30)
    subscription.save!

    paid_through = subscription.paid_through
    subscription.transactions.empty?.should be_true

    Freemium.configuration.gateway.stub!(:charge).and_return(
      Transaction.new(
        :billing_key => subscription.billing_key,
        :amount => subscription.rate,
        :success => true
      )
    )

    subscription.should_receive(:receive_payment).and_raise(RuntimeError)
    subscription.charge!
    subscription.reload.transactions.empty?.should be_false
  end

  it "should not charge a subscription" do
    subscription = Subscription.first
    paid_through = subscription.paid_through
    Freemium.configuration.gateway.stub!(:charge).and_return(
      Transaction.new(
        :billing_key => subscription.billing_key,
        :amount => subscription.rate,
        :success => false
      )
    )

    subscription.expire_on.should be_nil
    lambda { subscription.charge! }.should_not raise_error
    subscription.reload.paid_through.should eql(paid_through), "not extended"
    subscription.expire_on.should_not be_nil
    subscription.transactions.last.success?.should be_false
  end

  protected

  def create_billable_subscription(options = {})
    Subscription.create!({
      :subscription_plan => subscription_plans(:premium),
      :subscribable => User.new(:name => 'a'),
      :paid_through => Date.today - 1,
      :credit_card => CreditCard.sample
    }.merge(options))
  end
end