# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Reporting::Reports::Customers::Base do
  context "as a site admin" do
    let(:user) { create(:admin_user) }
    subject { described_class.new user, {} }

    describe "addresses report" do
      it "returns headers for addresses" do
        expect(subject.table_headers).to eq(["First Name", "Last Name", "Billing Address",
                                             "Email", "Phone", "Hub", "Hub Address",
                                             "Shipping Method", "Total Number of Orders",
                                             "Total incl. tax ($)",
                                             "Last completed order date"])
      end

      it "builds a table from a list of variants" do
        a = create(:address)
        d = create(:distributor_enterprise)
        o = create(:order, distributor: d, bill_address: a)
        o.shipments << create(:shipment)

        allow(subject).to receive(:query_result).and_return [[o]]
        expect(subject.table_rows).to eq([[
                                           a.firstname, a.lastname,
                                           [a.address1, a.address2, a.city].join(" "),
                                           o.email, a.phone, d.name,
                                           [d.address.address1, d.address.address2,
                                            d.address.city].join(" "),
                                           o.shipping_method.name, 1, o.total, "none"
                                         ]])
      end

      context "when there are multiple orders for the same customer" do
        let!(:a) { create(:bill_address) }
        let!(:d){ create(:distributor_enterprise) }
        let!(:sm) { create(:shipping_method, distributors: [d]) }
        let!(:customer) { create(:customer) }
        let!(:o1) {
          create(:order_with_totals_and_distribution, :completed, distributor: d,
                                                                  bill_address: a,
                                                                  shipping_method: sm,
                                                                  customer:)
        }
        let!(:o2) {
          create(:order_with_totals_and_distribution, :completed, distributor: d,
                                                                  bill_address: a,
                                                                  shipping_method: sm,
                                                                  customer:)
        }
        before do
          o1.update(completed_at: "2023-01-01")
          o2.update(completed_at: "2023-01-02")
          [o1, o2].each do |order|
            order.update!(email: "test@test.com")
          end
        end

        it "returns only one row per customer with the right data" do
          expect(subject.query_result).to match_array [[o1, o2]]
          expect(subject.table_rows.size).to eq(1)
          expect(subject.table_rows)
            .to eq([[
                     a.firstname, a.lastname,
                     [a.address1, a.address2, a.city].join(" "),
                     o1.email, a.phone, d.name,
                     [d.address.address1, d.address.address2, d.address.city].join(" "),
                     o1.shipping_method.name, 2, o1.total + o2.total, "2023-01-02"
                   ]])
        end

        context "orders from different hubs" do
          let!(:d2) { create(:distributor_enterprise) }
          let!(:sm2) { create(:shipping_method, distributors: [d2]) }
          let!(:o2) {
            create(:order_with_totals_and_distribution, :completed, distributor: d2,
                                                                    bill_address: a,
                                                                    shipping_method: sm2)
          }

          it "returns one row per customer per hub" do
            expect(subject.query_result.size).to eq(2)
            expect(subject.table_rows.size).to eq(2)
            expect(subject.table_rows)
              .to eq([[
                       a.firstname, a.lastname,
                       [a.address1, a.address2, a.city].join(" "),
                       o1.email, a.phone, d.name,
                       [d.address.address1, d.address.address2, d.address.city].join(" "),
                       o1.shipping_method.name, 1, o1.total, "2023-01-01"
                     ], [
                       a.firstname, a.lastname,
                       [a.address1, a.address2, a.city].join(" "),
                       o2.email, a.phone, d2.name,
                       [d2.address.address1, d2.address.address2, d2.address.city].join(" "),
                       o2.shipping_method.name, 1, o2.total, "2023-01-02"
                     ]])
          end
        end

        context "orders with different shipping methods" do
          let!(:sm2) { create(:shipping_method, distributors: [d], name: "Bike") }
          let!(:o2) {
            create(:order_with_totals_and_distribution, :completed, distributor: d,
                                                                    bill_address: a,
                                                                    shipping_method: sm2)
          }
          before do
            o2.select_shipping_method(sm2.id)
          end

          context "when the shipping method column is being included" do
            let(:fields_to_show) do
              [:first_name, :last_name, :billing_address, :email, :phone, :hub, :hub_address,
               :shipping_method, :total_orders, :total_incl_tax, :last_completed_order_date]
            end
            subject { described_class.new(user, { fields_to_show: }) }

            it "returns one row per customer per shipping method" do
              expect(subject.query_result.size).to eq(2)
              expect(subject.table_rows.size).to eq(2)
              expect(subject.table_rows).to eq(
                [
                  [
                    a.firstname,
                    a.lastname,
                    [a.address1, a.address2, a.city].join(" "),
                    o1.email,
                    a.phone,
                    d.name,
                    [d.address.address1, d.address.address2, d.address.city].join(" "),
                    o1.shipping_method.name, 1, o1.total, o1.completed_at.strftime("%Y-%m-%d")
                  ],
                  [
                    a.firstname,
                    a.lastname,
                    [a.address1, a.address2, a.city].join(" "),
                    o2.email,
                    a.phone,
                    d.name,
                    [d.address.address1, d.address.address2, d.address.city].join(" "),
                    sm2.name, 1, o2.total, o2.completed_at.strftime("%Y-%m-%d")
                  ]
                ]
              )
            end
          end

          context "when the shipping method column is not included in the report" do
            let(:fields_to_show) do
              [:first_name, :last_name, :billing_address, :email, :phone, :hub, :hub_address]
            end
            subject { described_class.new(user, { fields_to_show: }) }

            it "returns a single row for the customer, otherwise it would return two identical
                rows" do
              expect(subject.query_result.size).to eq(2)
              expect(subject.table_rows.size).to eq(1)
              expect(subject.table_rows).to eq(
                [[
                  a.firstname,
                  a.lastname,
                  [a.address1, a.address2, a.city].join(" "),
                  o1.email,
                  a.phone,
                  d.name,
                  [d.address.address1, d.address.address2, d.address.city].join(" ")
                ]]
              )
            end
          end
        end
      end
    end

    describe "fetching orders" do
      it "fetches completed orders" do
        o1 = create(:order)
        o2 = create(:order, completed_at: 1.day.ago)
        expect(subject.query_result).to eq([[o2]])
      end

      it "does not show cancelled orders" do
        o1 = create(:order, state: "canceled", completed_at: 1.day.ago)
        o2 = create(:order, completed_at: 1.day.ago)
        expect(subject.query_result).to eq([[o2]])
      end
    end
  end

  context "as an enterprise user" do
    let(:user) { create(:user) }

    subject { described_class.new user, {} }

    describe "fetching orders" do
      let(:supplier) { create(:supplier_enterprise) }
      let(:product) { create(:simple_product, supplier_id: supplier.id) }
      let(:order) { create(:order, completed_at: 1.day.ago) }

      it "only shows orders managed by the current user" do
        d1 = create(:distributor_enterprise)
        d1.enterprise_roles.build(user:).save
        d2 = create(:distributor_enterprise)
        d2.enterprise_roles.build(user: create(:user)).save

        o1 = create(:order, distributor: d1, completed_at: 1.day.ago)
        o2 = create(:order, distributor: d2, completed_at: 1.day.ago)

        expect(subject).to receive(:filter).with([o1]).and_return([o1])
        expect(subject.query_result).to eq([[o1]])
      end

      it "does not show orders through a hub that the current user does not manage" do
        # Given a supplier enterprise with an order for one of its products
        supplier.enterprise_roles.build(user:).save
        order.line_items << create(:line_item_with_shipment, product:)

        # When I fetch orders, I should see no orders
        expect(subject).to receive(:filter).with([]).and_return([])
        expect(subject.query_result).to eq([])
      end
    end

    describe "filtering orders" do
      let(:orders) { Spree::Order.where(nil) }
      let(:supplier) { create(:supplier_enterprise) }

      it "returns all orders sans-params" do
        expect(subject.filter(orders)).to eq(orders)
      end

      describe "filters to a specific completed_at date range" do
        let!(:o1) { create(:order, completed_at: 1.day.ago) }
        let!(:o2) { create(:order, completed_at: 3.days.ago) }
        let!(:o3) { create(:order, completed_at: 5.days.ago) }

        it do
          allow(subject).to receive(:params).and_return(
            q: {
              completed_at_gt: 1.day.before(o2.completed_at),
              completed_at_lt: 1.day.after(o2.completed_at)
            }
          )
          expect(subject.filter(orders)).to eq([o2])
        end

        it "when completed_at_gt param is missing" do
          allow(subject).to receive(:params).and_return(
            q: {
              completed_at_gt: "",
              completed_at_lt: 1.day.after(o2.completed_at)
            }
          )
          expect(subject.filter(orders)).to match_array [o2, o3]
        end

        it "when completed_at_lt param is missing" do
          allow(subject).to receive(:params).and_return(
            q: {
              completed_at_gt: 1.day.before(o2.completed_at),
              completed_at_lt: ""
            }
          )
          expect(subject.filter(orders)).to match_array [o1, o2]
        end
      end

      it "filters to a specific distributor" do
        d1 = create(:distributor_enterprise)
        d2 = create(:distributor_enterprise)
        order1 = create(:order, distributor: d1)
        order2 = create(:order, distributor: d2)

        allow(subject).to receive(:params).and_return(distributor_id: d1.id)
        expect(subject.filter(orders)).to eq([order1])
      end

      it "filters to a specific cycle" do
        oc1 = create(:simple_order_cycle)
        oc2 = create(:simple_order_cycle)
        order1 = create(:order, order_cycle: oc1)
        order2 = create(:order, order_cycle: oc2)

        allow(subject).to receive(:params).and_return(order_cycle_id: oc1.id)
        expect(subject.filter(orders)).to eq([order1])
      end
    end
  end
end
