# frozen_string_literal: true

require 'spec_helper'

RSpec.describe LineItemsController do
  let(:user) { create(:user) }
  let(:distributor) { create(:distributor_enterprise) }
  let(:order_cycle) { create(:simple_order_cycle) }

  context "listing bought items" do
    let!(:completed_order) do
      order = create(:completed_order_with_totals, user:, distributor:,
                                                   order_cycle:, line_items_count: 1)
      Orders::WorkflowService.new(order).complete!
      order
    end

    before do
      allow(controller).to receive_messages spree_current_user: user
      allow(controller).to receive_messages current_order_cycle: order_cycle
      allow(controller).to receive_messages current_distributor: distributor
    end

    it "lists items bought by the user from the same shop in the same order_cycle" do
      get :bought, format: :json
      expect(response).to have_http_status :ok
      json_response = response.parsed_body
      expect(json_response.length).to eq completed_order.line_items.reload.count
      expect(json_response[0]['id']).to eq completed_order.line_items.first.id
    end
  end

  describe "destroying a line item" do
    context "on a completed order" do
      let(:item) do
        order = create(:completed_order_with_totals)
        item = create(:line_item, order:)
        Orders::WorkflowService.new(order).complete!
        item
      end

      let(:order) { item.order }
      let(:order_cycle) {
        create(:simple_order_cycle, distributors: [distributor],
                                    variants: [order.line_item_variants])
      }

      before { allow(controller).to receive_messages spree_current_user: item.order.user }

      context "with a line item id" do
        let(:params) { { format: :json, id: item } }

        context "where the item's order is not associated with the user" do
          it "denies deletion" do
            delete(:destroy, params:)
            expect(response).to have_http_status :forbidden
          end
        end

        context "where the item's order is associated with the current user" do
          before do
            order.update!(user_id: user.id)
            allow(controller).to receive_messages spree_current_user: item.order.user
          end

          context "without an order cycle or distributor" do
            it "denies deletion" do
              delete(:destroy, params:)
              expect(response).to have_http_status :forbidden
            end
          end

          context "with an order cycle and distributor" do
            before { order.update!(order_cycle_id: order_cycle.id, distributor_id: distributor.id) }

            context "where changes are not allowed" do
              it "denies deletion" do
                delete(:destroy, params:)
                expect(response).to have_http_status :forbidden
              end
            end

            context "where changes are allowed" do
              before { distributor.update!(allow_order_changes: true) }

              it "deletes the line item" do
                delete(:destroy, params:)
                expect(response).to have_http_status :no_content
                expect { item.reload }.to raise_error ActiveRecord::RecordNotFound
              end

              context "after a payment is captured" do
                let(:payment) {
                  create(:check_payment, :completed, amount: order.total, order:)
                }
                before { payment.capture! }

                it 'updates the payment state' do
                  expect(order.payment_state).to eq 'paid'
                  delete(:destroy, params:)
                  order.reload
                  expect(order.payment_state).to eq 'credit_owed'
                end
              end
            end
          end
        end
      end
    end

    context "on a completed order with shipping and payment fees" do
      let(:zone) { create(:zone_with_member) }
      let(:shipping_tax_rate) do
        create(:tax_rate, included_in_price: true,
                          calculator: Calculator::DefaultTax.new,
                          amount: 0.25,
                          zone:)
      end
      let(:shipping_tax_category) { create(:tax_category, tax_rates: [shipping_tax_rate]) }
      let(:shipping_fee) { 3 }
      let(:payment_fee) { 5 }
      let(:distributor_with_taxes) { create(:distributor_enterprise_with_tax) }
      let(:order) {
        create(:completed_order_with_fees, distributor: distributor_with_taxes,
                                           shipping_fee:, payment_fee:,
                                           shipping_tax_category:)
      }

      before do
        allow(order).to receive(:tax_zone) { zone }
        order.reload
        order.create_tax_charge!
      end

      it "updates the fees" do
        # Sanity check fees
        item_num = order.line_items.length
        initial_fees = item_num * (shipping_fee + payment_fee)

        expect(order.shipment.adjustments.tax.count).to eq 1
        expect(order.shipment.included_tax_total).to eq 1.2

        # Delete the item
        item = order.line_items.first
        allow(controller).to receive_messages spree_current_user: order.user
        delete :destroy, format: :json, params: { id: item }
        expect(response).to have_http_status :no_content

        # Check the fees again
        order.reload
        order.shipment.reload
        expect(order.adjustment_total).to eq initial_fees - shipping_fee - payment_fee
        expect(order.shipment.adjustment_total).to eq shipping_fee
        expect(order.payments.first.adjustment.amount).to eq payment_fee
        expect(order.shipment.included_tax_total).to eq 0.6
      end
    end

    context "on a completed order with enterprise fees" do
      let(:user) { create(:user) }
      let(:variant1) { create(:variant) }
      let(:variant2) { create(:variant) }
      let(:distributor) { create(:distributor_enterprise, allow_order_changes: true) }
      let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) }
      let(:calculator) {
        Calculator::PriceSack.new(preferred_minimal_amount: 15, preferred_normal_amount: 22,
                                  preferred_discount_amount: 11)
      }
      let(:enterprise_fee) { create(:enterprise_fee, calculator:) }
      let!(:exchange) {
        create(:exchange, incoming: true, sender: variant1.supplier,
                          receiver: order_cycle.coordinator, variants: [variant1, variant2],
                          enterprise_fees: [enterprise_fee])
      }
      let!(:order) do
        order = create(:completed_order_with_totals, user:, distributor:,
                                                     order_cycle:, line_items_count: 2)
        order.reload.line_items.first.update(variant_id: variant1.id)
        order.line_items.last.update(variant_id: variant2.id)
        Orders::WorkflowService.new(order).complete!
        order.recreate_all_fees!
        order
      end
      let(:params) { { format: :json, id: order.line_items.first } }

      it "updates the fees" do
        expect(order.reload.adjustment_total).to eq calculator.preferred_discount_amount

        allow(controller).to receive_messages spree_current_user: user
        delete(:destroy, params:)
        expect(response).to have_http_status :no_content

        expect(order.reload.adjustment_total).to eq calculator.preferred_normal_amount
      end
    end
  end
end
