# frozen_string_literal: true

require 'spec_helper'

RSpec.describe SolidusSubscriptions::Installment, type: :model do
  let(:installment) { create :installment }

  it { is_expected.to validate_presence_of :subscription }

  describe '#out_of_stock' do
    subject(:out_of_stock) { installment.out_of_stock }

    let(:expected_date) do
      Time.zone.today + SolidusSubscriptions.configuration.reprocessing_interval
    end

    it { is_expected.to be_a SolidusSubscriptions::InstallmentDetail }
    it { is_expected.not_to be_successful }

    it 'has the correct message' do
      expect(out_of_stock).to have_attributes(
        message: I18n.t('solidus_subscriptions.installment_details.out_of_stock')
      )
    end

    it 'advances the installment actionable_date' do
      out_of_stock
      actionable_date = installment.reload.actionable_date
      expect(actionable_date).to eq expected_date
    end
  end

  describe '#success!' do
    subject(:success) { installment.success!(order) }

    let(:order) { create :order }

    let(:installment) { create :installment, actionable_date: actionable_date }
    let(:actionable_date) { 1.month.from_now.to_date }

    it 'removes any actionable date if any' do
      expect { success }.
        to change(installment, :actionable_date).
        from(actionable_date).to(nil)
    end

    it 'creates a new installment detail' do
      expect { success }.
        to change { SolidusSubscriptions::InstallmentDetail.count }.
        by(1)
    end

    it 'creates a successful installment detail' do
      success
      expect(installment.details.last).to be_successful && have_attributes(
        order: order,
        message: I18n.t('solidus_subscriptions.installment_details.success')
      )
    end
  end

  describe '#failed!' do
    subject(:failed) { installment.failed!(order) }

    let(:order) { create :order }

    let(:expected_date) do
      Time.zone.today + SolidusSubscriptions.configuration.reprocessing_interval
    end

    it { is_expected.to be_a SolidusSubscriptions::InstallmentDetail }
    it { is_expected.not_to be_successful }

    it 'has the correct message' do
      expect(failed).to have_attributes(
        message: I18n.t('solidus_subscriptions.installment_details.failed'),
        order: order
      )
    end

    it 'advances the installment actionable_date' do
      failed
      actionable_date = installment.reload.actionable_date
      expect(actionable_date).to eq expected_date
    end

    context 'when the reprocessing interval is set to nil' do
      before { stub_config(reprocessing_interval: nil) }

      it 'does not advance the installment actionable_date' do
        failed
        actionable_date = installment.reload.actionable_date
        expect(actionable_date).to be_nil
      end
    end
  end

  describe '#unfulfilled?' do
    subject { installment.unfulfilled? }

    let(:installment) { create(:installment, details: details) }

    context 'when the installment has an associated successful detail' do
      let(:details) { create_list :installment_detail, 1, success: true }

      it { is_expected.to be_falsy }
    end

    context 'when the installment has no associated successful detail' do
      let(:details) { create_list :installment_detail, 1 }

      it { is_expected.to be_truthy }
    end
  end

  describe '#fulfilled' do
    subject { installment.fulfilled? }

    let(:installment) { create(:installment, details: details) }

    context 'when the installment has an associated completed order' do
      let(:details) { create_list :installment_detail, 1, success: true }

      it { is_expected.to be_truthy }
    end

    context 'when the installment has no associated completed order' do
      let(:details) { create_list :installment_detail, 1 }

      it { is_expected.to be_falsy }
    end
  end

  describe '#payment_failed!' do
    context 'when the maximum reprocessing time has been reached' do
      it 'creates a new installment detail' do
        subscription = create(:subscription)
        allow(subscription).to receive(:maximum_reprocessing_time_reached?).and_return(true)

        current_installment = create(:installment, subscription: subscription)
        current_installment.payment_failed!(create(:order))

        expect(current_installment.details.count).to eq(1)
      end

      it 'sets the actionable_date to nil' do
        subscription = create(:subscription)
        allow(subscription).to receive(:maximum_reprocessing_time_reached?).and_return(true)

        current_installment = create(:installment, subscription: subscription)
        current_installment.payment_failed!(create(:order))

        expect(current_installment.actionable_date).to eq(nil)
      end

      it 'cancels the subscription' do
        subscription = create(:subscription)
        allow(subscription).to receive(:maximum_reprocessing_time_reached?).and_return(true)

        current_installment = create(:installment, subscription: subscription)
        current_installment.payment_failed!(create(:order))

        expect(current_installment.subscription.state).to eq('canceled')
      end
    end

    context 'when the maximum reprocessing time has not been reached' do
      it 'creates a new installment detail' do
        subscription = create(:subscription)
        allow(subscription).to receive(:maximum_reprocessing_time_reached?).and_return(false)

        current_installment = create(:installment, subscription: subscription)
        current_installment.payment_failed!(create(:order))
        current_installment.payment_failed!(create(:order))

        expect(current_installment.details.count).to eq(2)
      end

      it "advances the installment's actionable_date" do
        subscription = create(:subscription)
        allow(subscription).to receive(:maximum_reprocessing_time_reached?).and_return(false)
        stub_config(reprocessing_interval: 2.days)

        current_installment = create(:installment, subscription: subscription)
        current_installment.payment_failed!(create(:order))

        expect(current_installment.actionable_date).to eq(Time.zone.today + 2.days)
      end

      it 'does not cancel the subscription' do
        subscription = create(:subscription)
        allow(subscription).to receive(:maximum_reprocessing_time_reached?).and_return(false)

        current_installment = create(:installment, subscription: subscription)
        current_installment.payment_failed!(create(:order))

        expect(subscription.state).to eq('active')
      end
    end
  end
end