# frozen_string_literal: true

require 'spec_helper'

RSpec.describe VoucherAdjustmentsService do
  describe '#update' do
    let(:enterprise) { build(:enterprise) }

    context 'with a flat rate voucher' do
      let(:voucher) do
        create(:voucher_flat_rate, code: 'new_code', enterprise:, amount: 10)
      end

      context 'when voucher covers the order total' do
        subject { order.voucher_adjustments.first }

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

        it 'updates the adjustment amount to -order.total' do
          voucher.create_adjustment(voucher.code, order)
          order.update_columns(item_total: 6)

          VoucherAdjustmentsService.new(order).update

          expect(subject.amount.to_f).to eq(-6.0)
        end
      end

      context 'with tax included in order price' do
        subject { order.voucher_adjustments.first }

        let(:order) do
          create(
            :order_with_taxes,
            distributor: enterprise,
            ship_address: create(:address),
            product_price: 110,
            tax_rate_amount: 0.10,
            included_in_price: true,
            tax_rate_name: "Tax 1"
          )
        end

        before do
          add_voucher(order, voucher)

          VoucherAdjustmentsService.new(order).update
        end

        it 'updates the adjustment included_tax' do
          # voucher_rate = amount / order.total
          # -10 / 160 = -0.0625
          # included_tax = voucher_rate * order.included_tax_total
          # -0.625 * 10 = -0.63
          expect(subject.included_tax.to_f).to eq(-0.63)
        end

        context "when re calculating" do
          it "does not update the adjustment amount" do
            expect do
              VoucherAdjustmentsService.new(order).update
            end.not_to change { subject.reload.amount }
          end

          it "does not update the tax adjustment" do
            expect do
              VoucherAdjustmentsService.new(order).update
            end.not_to change { subject.reload.included_tax }
          end

          context "when the order changed" do
            before do
              order.update_columns(item_total: 200)
            end

            it "does update the adjustment amount" do
              expect do
                VoucherAdjustmentsService.new(order).update
              end.not_to change { subject.reload.amount }
            end

            it "updates the tax adjustment" do
              expect do
                VoucherAdjustmentsService.new(order).update
              end.to change { subject.reload.included_tax }
            end
          end
        end
      end

      context 'with tax not included in order price' do
        let(:order) do
          create(
            :order_with_taxes,
            distributor: enterprise,
            ship_address: create(:address),
            product_price: 110,
            tax_rate_amount: 0.10,
            included_in_price: false,
            tax_rate_name: "Tax 1"
          )
        end
        let(:adjustment) { order.voucher_adjustments.first }
        let(:tax_adjustment) { order.voucher_adjustments.second }

        before do
          add_voucher(order, voucher)

          VoucherAdjustmentsService.new(order).update
        end

        it 'includes amount without tax' do
          # voucher_rate = amount / order.total
          # -10 / 171 = -0.058479532
          # amount = voucher_rate * (order.total - order.additional_tax_total)
          # -0.058479532 * (171 -11) = -9.36
          expect(adjustment.amount.to_f).to eq(-9.36)
        end

        it 'creates a tax adjustment' do
          # voucher_rate = amount / order.total
          # -10 / 171 = -0.058479532
          # amount = voucher_rate * order.additional_tax_total
          # -0.058479532 * 11 = -0.64
          expect(tax_adjustment.amount.to_f).to eq(-0.64)
          expect(tax_adjustment.label).to match("Tax")

          expect(tax_adjustment.metadata.enterprise_id).to eq(
            tax_adjustment.originator.enterprise.id
          )
          expect(tax_adjustment.metadata.fee_name).to eq("Tax")
          expect(tax_adjustment.metadata.fee_type).to eq("Voucher")
        end

        context "when re calculating" do
          it "does not update the adjustment amount" do
            expect do
              VoucherAdjustmentsService.new(order).update
            end.not_to change { adjustment.reload.amount }
          end

          it "does not update the tax adjustment" do
            expect do
              VoucherAdjustmentsService.new(order).update
            end.not_to change { tax_adjustment.reload.amount }
          end

          context "when the order changed" do
            before do
              order.update_columns(item_total: 200)
            end

            it "updates the adjustment amount" do
              expect do
                VoucherAdjustmentsService.new(order).update
              end.to change { adjustment.reload.amount }
            end

            it "updates the tax adjustment" do
              expect do
                VoucherAdjustmentsService.new(order).update
              end.to change { tax_adjustment.reload.amount }
            end
          end
        end
      end
    end

    context 'with a percentage rate voucher' do
      let(:voucher) do
        create(:voucher_percentage_rate, code: 'new_code', enterprise:, amount: 10)
      end
      let(:adjustment) { order.voucher_adjustments.first }
      let(:tax_adjustment) { order.voucher_adjustments.second }

      context 'when voucher covers the order total' do
        subject { order.voucher_adjustments.first }

        let(:voucher) do
          create(:voucher_percentage_rate, code: 'new_code', enterprise:, amount: 100)
        end
        let(:order) { create(:order_with_totals) }

        it 'updates the adjustment amount to -order.total' do
          voucher.create_adjustment(voucher.code, order)

          order.update_columns(item_total: 150)

          VoucherAdjustmentsService.new(order).update

          expect(subject.amount.to_f).to eq(-150.0)
        end
      end

      context 'with tax included in order price' do
        subject { order.voucher_adjustments.first }

        let(:order) do
          create(
            :order_with_taxes,
            distributor: enterprise,
            ship_address: create(:address),
            product_price: 110,
            tax_rate_amount: 0.10,
            included_in_price: true,
            tax_rate_name: "Tax 1"
          )
        end

        before do
          add_voucher(order, voucher)

          VoucherAdjustmentsService.new(order).update
        end

        it 'updates the adjustment included_tax' do
          # voucher_rate = voucher.amount / 100
          # 10 / 100 = 0.1
          # included_tax = -voucher_rate * included_tax_total
          # -0.1 * 10 = -1
          expect(subject.included_tax.to_f).to eq(-1)
        end
      end

      context 'with tax not included in order price' do
        let(:order) do
          create(
            :order_with_taxes,
            distributor: enterprise,
            ship_address: create(:address),
            product_price: 110,
            tax_rate_amount: 0.10,
            included_in_price: false,
            tax_rate_name: "Tax 1"
          )
        end

        before do
          add_voucher(order, voucher)

          VoucherAdjustmentsService.new(order).update
        end

        it 'includes amount without tax' do
          adjustment = order.voucher_adjustments.first
          # voucher_rate = voucher.amount / 100
          # 10 / 100 = 0.1
          # amount = -voucher_rate * (order.total - order.additional_tax_total)
          # -0.1 * (171 -11) = -16.0
          expect(adjustment.amount.to_f).to eq(-16.0)
        end

        it 'creates a tax adjustment' do
          # voucher_rate = voucher.amount / 100
          # 10 / 100 = 0.1
          # amount = -voucher_rate * order.additional_tax_total
          # -0.1 * 11 = -1.1
          tax_adjustment = order.voucher_adjustments.second
          expect(tax_adjustment.amount.to_f).to eq(-1.1)
          expect(tax_adjustment.label).to match("Tax")
        end
      end
    end

    context 'when no order given' do
      it "doesn't blow up" do
        expect { VoucherAdjustmentsService.new(nil).update }.not_to raise_error
      end
    end

    context 'when no voucher used on the given order' do
      let(:order) { create(:order_with_line_items, line_items_count: 1, distributor: enterprise) }

      it "doesn't blow up" do
        expect { VoucherAdjustmentsService.new(order).update }.not_to raise_error
      end
    end
  end

  describe "#voucher_included_tax" do
    subject(:voucher_included_tax) { VoucherAdjustmentsService.new(order).voucher_included_tax }

    let(:order) { create(:order_with_totals) }
    let(:enterprise) { build(:enterprise) }
    let(:voucher) do
      create(:voucher_flat_rate, code: 'new_code', enterprise:, amount: 10)
    end

    it "returns included tax from voucher adjusment" do
      voucher_adjustment = voucher.create_adjustment(voucher.code, order)
      # Manually update included tax, so we don't have to do a big data setup to be able to use
      # VoucherAdjustmentsService.update
      voucher_adjustment.update(included_tax: 0.5)

      expect(voucher_included_tax).to eq(0.5)
    end

    context "When no voucher adjustment linked to the order" do
      it "returns 0.0" do
        expect(voucher_included_tax).to eq(0.0)
      end
    end
  end

  describe "#voucher_excluded_tax" do
    subject(:voucher_excluded_tax) { VoucherAdjustmentsService.new(order).voucher_excluded_tax }
    let(:order) do
      create(
        :order_with_taxes,
        distributor: enterprise,
        ship_address: create(:address),
        product_price: 110,
        tax_rate_amount: 0.10,
        included_in_price: false,
        tax_rate_name: "Tax 1"
      )
    end
    let(:enterprise) { build(:enterprise) }
    let(:voucher) do
      create(:voucher_flat_rate, code: 'new_code', enterprise:, amount: 10)
    end

    it "returns the amount from the tax voucher adjustment" do
      add_voucher(order, voucher)

      VoucherAdjustmentsService.new(order).update

      expect(voucher_excluded_tax).to eq(-0.64)
    end

    context "when no voucher adjustment tax" do
      it "returns 0" do
        voucher_adjustment = voucher.create_adjustment(voucher.code, order)

        expect(voucher_excluded_tax).to eq(0.0)
      end
    end

    context "when no voucher adjustment linked to the order" do
      it "returns 0.0" do
        expect(voucher_excluded_tax).to eq(0.0)
      end
    end
  end

  def add_voucher(order, voucher)
    voucher.create_adjustment(voucher.code, order)

    # Update taxes
    order.create_tax_charge!
    order.update_shipping_fees!
    order.update_order!
  end
end
