# frozen_string_literal: true

RSpec.describe Orders::AvailablePaymentMethodsService do
  context "when the order has no current_distributor" do
    it "returns an empty array" do
      order_cycle = create(:sells_own_order_cycle)
      order = build(:order, distributor: nil, order_cycle:)

      expect(Orders::AvailablePaymentMethodsService.new(order).to_a).to eq []
    end
  end

  it "does not return 'back office only' payment method" do
    distributor = create(:distributor_enterprise)
    frontend_payment_method = create(:payment_method, distributors: [distributor])
    backoffice_only_payment_method = create(:payment_method,
                                            distributors: [distributor],
                                            display_on: 'back_end')
    order_cycle = create(:sells_own_order_cycle)
    order = build(:order, distributor:, order_cycle:)

    available_payment_methods = Orders::AvailablePaymentMethodsService.new(order).to_a

    expect(available_payment_methods).to eq [frontend_payment_method]
  end

  it "does not return payment methods which are not configured correctly" do
    distributor = create(:distributor_enterprise)
    frontend_payment_method = create(:payment_method, distributors: [distributor])
    unconfigured_payment_method = create(:stripe_sca_payment_method,
                                         distributors: [distributor],
                                         display_on: 'back_end')
    order_cycle = create(:sells_own_order_cycle)
    order = build(:order, distributor:, order_cycle:)

    available_payment_methods = Orders::AvailablePaymentMethodsService.new(order).to_a

    expect(available_payment_methods).to eq [frontend_payment_method]
  end

  it "does not return payment methods which are internal" do
    distributor = create(:distributor_enterprise)
    frontend_payment_method = create(:payment_method, distributors: [distributor])
    internal_payment_method = create(:customer_credit_payment_method)

    order_cycle = create(:sells_own_order_cycle)
    order = build(:order, distributor:, order_cycle:)

    available_payment_methods = Orders::AvailablePaymentMethodsService.new(order).to_a

    expect(available_payment_methods).to eq [frontend_payment_method]
  end

  context "when no tag rules are in effect" do
    context "sells own order cycle i.e. simple" do
      it "only returns the payment methods which are available on the order cycle
          and belong to the order distributor" do
        distributor_i = create(:distributor_enterprise)
        distributor_ii = create(:distributor_enterprise)
        distributor_iii = create(:distributor_enterprise)
        payment_method_i = create(:payment_method, distributors: [distributor_i])
        payment_method_ii = create(:payment_method, distributors: [distributor_ii])
        payment_method_iii = create(:payment_method, distributors: [distributor_iii])
        order_cycle = create(:sells_own_order_cycle, distributors: [distributor_i, distributor_ii])
        order = build(:order, distributor: distributor_i, order_cycle:)

        available_payment_methods = Orders::AvailablePaymentMethodsService.new(order).to_a

        expect(available_payment_methods).to eq [payment_method_i]
      end
    end

    context "distributor order cycle i.e. not simple" do
      it "only returns the payment methods which are available on the order cycle
          and belong to the order distributor" do
        distributor_i = create(:distributor_enterprise, payment_methods: [])
        distributor_ii = create(:distributor_enterprise, payment_methods: [])
        payment_method_i = create(:payment_method, distributors: [distributor_i])
        payment_method_ii = create(:payment_method, distributors: [distributor_i])
        payment_method_iii = create(:payment_method, distributors: [distributor_ii])
        payment_method_iv = create(:payment_method, distributors: [distributor_ii])
        order_cycle = create(:distributor_order_cycle,
                             distributors: [distributor_i, distributor_ii])
        order_cycle.selected_distributor_payment_methods << [
          distributor_i.distributor_payment_methods.last,
          distributor_ii.distributor_payment_methods.last
        ]
        order = build(:order, distributor: distributor_i, order_cycle:)

        available_payment_methods = Orders::AvailablePaymentMethodsService.new(order).to_a

        expect(available_payment_methods).to eq [payment_method_ii]
      end
    end
  end

  context "when FilterPaymentMethods tag rules are in effect" do
    let(:user) { create(:user) }
    let(:distributor) { create(:distributor_enterprise) }
    let(:other_distributor) { create(:distributor_enterprise) }
    let!(:distributor_payment_method) { create(:payment_method, distributors: [distributor]) }
    let!(:other_distributor_payment_method) do
      create(:payment_method, distributors: [other_distributor])
    end
    let(:customer) { create(:customer, user:, enterprise: distributor) }
    let!(:tag_rule) {
      create(:filter_payment_methods_tag_rule,
             enterprise: distributor,
             preferred_customer_tags: "local",
             preferred_payment_method_tags: "local-delivery")
    }
    let!(:default_tag_rule) {
      create(:filter_payment_methods_tag_rule,
             enterprise: distributor,
             is_default: true,
             preferred_payment_method_tags: "local-delivery")
    }
    let!(:tagged_payment_method) { distributor_payment_method }
    let!(:untagged_payment_method) { other_distributor_payment_method }

    before do
      tagged_payment_method.update_attribute(:tag_list, 'local-delivery')
      distributor.payment_methods = [tagged_payment_method, untagged_payment_method]
    end

    context "with a preferred visiblity of 'visible', default visibility of 'hidden'" do
      before {
        tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'visible')
      }
      before {
        default_tag_rule.update_attribute(:preferred_matched_payment_methods_visibility,
                                          'hidden')
      }

      let(:order_cycle) { create(:sells_own_order_cycle) }
      let(:order) { build(:order, distributor:, order_cycle:) }

      context "when the customer is nil" do
        let(:available_payment_methods) { Orders::AvailablePaymentMethodsService.new(order).to_a }

        it "applies default action (hide)" do
          expect(available_payment_methods).to include untagged_payment_method
          expect(available_payment_methods).not_to include tagged_payment_method
        end
      end

      context "when a customer is present" do
        let(:available_payment_methods) {
          Orders::AvailablePaymentMethodsService.new(order, customer).to_a
        }

        context "and the customer's tags match" do
          before do
            customer.update_attribute(:tag_list, 'local')
          end

          it "applies the action (show)" do
            expect(available_payment_methods).to include(
              tagged_payment_method,
              untagged_payment_method
            )
          end
        end

        context "and the customer's tags don't match" do
          before do
            customer.update_attribute(:tag_list, 'something')
          end

          it "applies the default action (hide)" do
            expect(available_payment_methods).to include untagged_payment_method
            expect(available_payment_methods).not_to include tagged_payment_method
          end
        end
      end
    end

    context "with a preferred visiblity of 'hidden', default visibility of 'visible'" do
      before {
        tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'hidden')
      }
      before {
        default_tag_rule.update_attribute(:preferred_matched_payment_methods_visibility,
                                          'visible')
      }

      let(:order_cycle) { create(:sells_own_order_cycle) }
      let(:order) { build(:order, distributor:, order_cycle:) }

      context "when the customer is nil" do
        let(:available_payment_methods) { Orders::AvailablePaymentMethodsService.new(order).to_a }

        it "applies default action (show)" do
          expect(available_payment_methods).to include(
            tagged_payment_method,
            untagged_payment_method
          )
        end
      end

      context "when a customer is present" do
        let(:available_payment_methods) {
          Orders::AvailablePaymentMethodsService.new(order, customer).to_a
        }

        context "and the customer's tags match" do
          before do
            customer.update_attribute(:tag_list, 'local')
          end

          it "applies the action (hide)" do
            expect(available_payment_methods).to include untagged_payment_method
            expect(available_payment_methods).not_to include tagged_payment_method
          end
        end

        context "and the customer's tags don't match" do
          before do
            customer.update_attribute(:tag_list, 'something')
          end

          it "applies the default action (show)" do
            expect(available_payment_methods).to include(
              tagged_payment_method,
              untagged_payment_method
            )
          end
        end
      end
    end
  end

  context "when two distributors implement the same payment methods" do
    context "only one distributor supports the two payment methods in the order cycle" do
      let(:oc){ create(:order_cycle) }
      let(:payment_method){ create(:payment_method) }
      let(:payment_method2){ create(:payment_method) }
      let(:d1){ oc.distributors.first }
      let(:d2){ oc.distributors.second }
      before {
        d1.payment_methods << payment_method
        d1.payment_methods << payment_method2
        d2.payment_methods << payment_method
        d2.payment_methods << payment_method2
        oc.selected_distributor_payment_methods << d1.distributor_payment_methods.second_to_last
        oc.selected_distributor_payment_methods << d1.distributor_payment_methods.last
        oc.selected_distributor_payment_methods << d2.distributor_payment_methods.last
      }
      it do
        order = build(:order, distributor: d2, order_cycle: oc)
        order_available_payment_methods = Orders::AvailablePaymentMethodsService.new(order).to_a
        expect(order_available_payment_methods).to eq([d2.payment_methods.last])
      end
    end
  end
end
