# frozen_string_literal: true

require 'rails_helper'

RSpec.describe Spree::UnitCancel do
  let(:unit_cancel) { described_class.create!(inventory_unit:, reason: described_class::SHORT_SHIP) }
  let(:inventory_unit) { create(:inventory_unit) }

  describe '#adjust!' do
    subject { unit_cancel.adjust! }

    it "creates an adjustment with the correct attributes" do
      expect { subject }.to change{ Spree::Adjustment.count }.by(1)

      adjustment = Spree::Adjustment.last
      expect(adjustment.adjustable).to eq inventory_unit.line_item
      expect(adjustment.amount).to eq(-10.0)
      expect(adjustment.order).to eq inventory_unit.order
      expect(adjustment.label).to eq "Cancellation - Short Ship"
      expect(adjustment).to be_eligible
      expect(adjustment).to be_finalized
    end

    context "when an adjustment has already been created" do
      before { unit_cancel.adjust! }

      it "raises" do
        expect { subject }.to raise_error("Adjustment is already created")
      end
    end
  end

  describe '#compute_amount' do
    subject { unit_cancel.compute_amount(line_item) }

    let(:line_item) { inventory_unit.line_item }
    let!(:inventory_unit2) { create(:inventory_unit, line_item: inventory_unit.line_item) }

    context "all inventory on the line item are not canceled" do
      it "divides the line item total by the inventory units size" do
        expect(subject).to eq(-5.0)
      end
    end

    context "some inventory on the line item is canceled" do
      before { inventory_unit2.cancel! }

      it "divides the line item total by the uncanceled units size" do
        expect(subject).to eq(-10.0)
      end

      it "raises an error if dividing by 0" do
        inventory_unit.cancel!
        expect { subject }.to raise_error ZeroDivisionError, "Line Item does not have any inventory units available to cancel"
      end
    end

    context "it is called with a line item that doesnt belong to the inventory unit" do
      let(:line_item) { create(:line_item) }

      it "raises an error" do
        expect { subject }.to raise_error RuntimeError, "Adjustable does not match line item"
      end
    end

    context "multiple inventory units" do
      let(:quantity) { 4 }
      let(:order) { create(:order_with_line_items, line_items_attributes: [{ quantity: }]) }
      let(:line_item) { order.line_items.first }
      let(:inventory_units) { line_item.inventory_units }

      it "has the right amount of inventory units" do
        expect(inventory_units.size).to eq quantity
      end

      it "properly creates adjustments for line_item" do
        inventory_units.each do |inventory_unit|
          described_class.create!(inventory_unit:, reason: described_class::SHORT_SHIP).adjust!
          inventory_unit.cancel!
        end
        expect(line_item.reload.total.to_d).to eq(0)
      end
    end

    context 'when line item has additional taxes' do
      let(:world_zone) { create(:zone, :with_country) }
      let(:tax_category) { create :tax_category }
      let(:product) { create :product, tax_category: }
      let!(:additional_tax_rate) do
        create(
          :tax_rate,
          name: 'Additional tax',
          tax_categories: [tax_category],
          zone: world_zone,
          included_in_price: false,
          amount: 0.15
        )
      end
      let(:shipping_category) { create :shipping_category }
      let(:shipping_method) do
        create :shipping_method,
               cost: 8.00,
               shipping_categories: [shipping_category],
               tax_category:,
               zones: [world_zone]
      end
      let(:shipping_address) { create :address, country_iso_code: world_zone.countries.first.iso }
      let(:order) do
        create(
          :order_with_line_items,
          ship_address: shipping_address,
          line_items_attributes: [{ product: }]
        )
      end
      let(:line_item) { order.line_items.first }
      let(:inventory_unit) { line_item.inventory_units.first }

      before { order.recalculate }

      it 'does not include line item additional taxes' do
        expect(line_item.additional_tax_total).not_to eq 0
        expect(subject).to eq(-5.0)
      end
    end
  end
end
