# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Spree::OrderMailer do
  describe '#confirm_email_for_customer' do
    subject(:email) { described_class.confirm_email_for_customer(order) }

    let(:order) { build(:order_with_totals_and_distribution) }

    it 'renders the shared/_payment.html.haml partial' do
      expect(email.body).to include('Payment summary')
    end

    context 'when the order has outstanding balance' do
      before { allow(order).to receive(:new_outstanding_balance) { 123 } }

      it 'renders the amount as money' do
        expect(email.body).to include('$123')
      end
    end

    context 'when the order has no outstanding balance' do
      before { allow(order).to receive(:new_outstanding_balance) { 0 } }

      it 'displays the payment status' do
        expect(email.body).to include('NOT PAID')
      end
    end

    context "when :from is not set explicitly" do
      it "falls back to spree config" do
        expect(email.from).to eq [Spree::Config[:mails_from]]
      end
    end

    it "doesn't aggressively escape double quotes body" do
      expect(email.body).not_to include("&quot;")
    end

    it "accepts an order id as an alternative to an Order object" do
      expect(Spree::Order).to receive(:find).with(order.id).and_return(order)
      expect {
        described_class.confirm_email_for_customer(order.id).deliver_now
      }.not_to raise_error
    end

    it "display the OFN header by default" do
      expect(email.body).to include(ContentConfig.url_for(:logo))
    end

    context 'when hide OFN navigation is enabled for the distributor of the order' do
      before do
        allow(order.distributor).to receive(:hide_ofn_navigation).and_return(true)
      end

      it 'does not display the OFN navigation' do
        expect(email.body).not_to include(ContentConfig.url_for(:logo))
      end
    end
  end

  describe '#confirm_email_for_shop' do
    subject(:email) { described_class.confirm_email_for_shop(order) }

    let(:order) { build(:order_with_totals_and_distribution) }

    it 'renders the shared/_payment.html.haml partial' do
      expect(email.body).to include('Payment summary')
    end

    it "sets a reply-to of the customer email" do
      expect(email.reply_to).to eq([order.email])
    end

    context 'when the order has outstanding balance' do
      before { allow(order).to receive(:new_outstanding_balance) { 123 } }

      it 'renders the amount as money' do
        expect(email.body).to include('$123')
      end
    end

    context 'when the order has no outstanding balance' do
      it 'displays the payment status' do
        expect(email.body).to include('NOT PAID')
      end
    end
  end

  context "basic behaviour" do
    let(:order) { build(:order_with_totals_and_distribution) }

    it "cancel_email accepts an order id as an alternative to an Order object" do
      expect(Spree::Order).to receive(:find).with(order.id).and_return(order)
      expect {
        Spree::OrderMailer.cancel_email(order.id).deliver_now
      }.not_to raise_error
    end
  end

  context "only shows eligible adjustments in emails" do
    let(:order) { create(:order_with_totals_and_distribution) }

    before do
      order.adjustments.create(
        label: "Eligible Adjustment",
        amount: 10,
        eligible: true
      )

      order.adjustments.create!(
        label: "Ineligible Adjustment",
        amount: 0,
      )
    end

    let!(:confirmation_email) { Spree::OrderMailer.confirm_email_for_customer(order) }
    let!(:cancel_email) { Spree::OrderMailer.cancel_email(order) }

    specify do
      expect(confirmation_email.body).not_to include("Ineligible Adjustment")
    end

    specify do
      expect(cancel_email.body).not_to include("Ineligible Adjustment")
    end
  end

  context "displays line item price" do
    let(:order) { create(:order_with_totals_and_distribution, :completed) }

    specify do
      confirmation_email = Spree::OrderMailer.confirm_email_for_customer(order)
      expect(confirmation_email.body).to include("3.00")
    end

    specify do
      cancel_email = Spree::OrderMailer.cancel_email(order)
      expect(cancel_email.body).to include("3.00")
    end
  end

  describe "#cancel_email_for_shop" do
    let(:distributor) { create(:distributor_enterprise) }
    let(:order) { create(:order, distributor:, state: "canceled") }
    let(:admin_order_link_href) { "href=\"#{spree.edit_admin_order_url(order)}\"" }
    let(:mail) { Spree::OrderMailer.cancel_email_for_shop(order) }

    it "sends an email to the distributor" do
      expect(mail.to).to eq([distributor.contact.email])
    end

    it "includes a link to the cancelled order in admin" do
      expect(mail.body).to match /#{admin_order_link_href}/
    end

    it "sets a reply-to of the customer email" do
      expect(mail.reply_to).to eq([order.email])
    end
  end

  describe "#cancel_email (for_customer)" do
    let(:distributor) { create(:distributor_enterprise) }
    let(:order) { create(:order, distributor:, state: "canceled") }
    let(:mail) { Spree::OrderMailer.cancel_email(order) }

    it "sends an email to the customer" do
      expect(mail.to).to eq([order.email])
    end

    it "displays the order number" do
      expect(mail.body).to include(order.number.to_s)
    end

    it "sets a reply-to of the customer email" do
      expect(mail.reply_to).to eq([order.distributor.contact.email])
    end
  end

  describe "order confimation" do
    let(:bill_address) { create(:address) }
    let(:distributor_address) {
      create(:address, address1: "distributor address", city: 'The Shire', zipcode: "1234")
    }
    let(:distributor) { create(:distributor_enterprise, address: distributor_address) }
    let(:shipping_instructions) { "pick up on thursday please!" }
    let(:ship_address) {
      create(:address, address1: "distributor address", city: 'The Shire', zipcode: "1234")
    }
    let(:order) {
      create(:order_with_line_items, distributor:, bill_address:,
                                     ship_address:,
                                     special_instructions: shipping_instructions)
    }

    describe "for customers" do
      it "should send an email to the customer when given an order" do
        Spree::OrderMailer.confirm_email_for_customer(order.id).deliver_now
        expect(ActionMailer::Base.deliveries.count).to eq(1)
        expect(ActionMailer::Base.deliveries.first.to).to eq([order.email])
      end

      it "should include SKUs" do
        mail = Spree::OrderMailer.confirm_email_for_customer(order.id)

        expect(mail.body.encoded).to include "SKU"
        expect(mail.body.encoded).to include order.line_items.first.variant.sku
      end

      it "sets a reply-to of the enterprise email" do
        Spree::OrderMailer.confirm_email_for_customer(order.id).deliver_now
        expect(ActionMailer::Base.deliveries.first.reply_to).to eq([distributor.contact.email])
      end

      it "includes a link to the configured instance email address" do
        mail = Spree::OrderMailer.confirm_email_for_customer(order.id)

        expect(mail.body.encoded).to include "mailto:hello@openfoodnetwork.org"
      end

      it "includes a link to the OFN global website if no email address is available" do
        expect(ContentConfig).to receive(:footer_email).and_return("")
        mail = Spree::OrderMailer.confirm_email_for_customer(order.id)

        expect(mail.body.encoded).to include "https://www.openfoodnetwork.org"
      end
    end

    describe "for shops" do
      it "sends an email to the shop owner when given an order" do
        Spree::OrderMailer.confirm_email_for_shop(order.id).deliver_now
        expect(ActionMailer::Base.deliveries.count).to eq(1)
        expect(ActionMailer::Base.deliveries.first.to).to eq([distributor.contact.email])
      end

      it "sends an email even if a footer_email is given" do
        # Testing bug introduced by a9c37c162e1956028704fbdf74ce1c56c5b3ce7d
        ContentConfig.footer_email = "email@example.com"
        Spree::OrderMailer.confirm_email_for_shop(order.id).deliver_now
        expect(ActionMailer::Base.deliveries.count).to eq(1)
      end
    end
  end

  describe "#invoice_email" do
    subject(:email) { described_class.invoice_email(order) }
    let(:order) { create(:completed_order_with_totals) }
    let!(:invoice_data_generator){ InvoiceDataGenerator.new(order) }
    let!(:invoice){
      create(:invoice, order:,
                       data: invoice_data_generator.serialize_for_invoice)
    }

    let(:generator){ instance_double(Orders::GenerateInvoiceService) }
    let(:renderer){ instance_double(InvoiceRenderer) }
    let(:attachment_filename){ "invoice-#{order.number}.pdf" }
    let(:deliveries){ ActionMailer::Base.deliveries }
    before do
      allow(Orders::GenerateInvoiceService).to receive(:new).with(order).and_return(generator)
      allow(InvoiceRenderer).to receive(:new).and_return(renderer)
    end
    context "When invoices feature is not enabled" do
      it "should call the invoice render with order as argument" do
        expect(generator).not_to receive(:generate_or_update_latest_invoice)
        expect(order).not_to receive(:invoices)
        expect(renderer).to receive(:render_to_string).with(order, nil).and_return("invoice")
        expect {
          email.deliver_now
        }.not_to raise_error
        expect(deliveries.count).to eq(1)
        expect(deliveries.first.attachments.count).to eq(1)
        expect(deliveries.first.attachments.first.filename).to eq(attachment_filename)
        expect(email.reply_to).to eq([order.distributor.contact.email])
      end
    end

    context "When invoices feature is enabled", feature: :invoices do
      it "should call the invoice renderer with invoice's presenter as argument" do
        expect(generator).to receive(:generate_or_update_latest_invoice)
        expect(order).to receive(:invoices).and_return([invoice])
        expect(renderer).to receive(:render_to_string).with(invoice.presenter, nil)
        email.deliver_now
      end
    end
  end

  context "display adjustments" do
    let(:order) { create(:order_with_totals_and_distribution, :completed) }
    let(:voucher) { create(:voucher, enterprise: order.distributor) }

    before do
      voucher.create_adjustment(voucher.code, order)
      OrderManagement::Order::Updater.new(order).update_voucher
    end

    let!(:confirmation_email_for_customer) { Spree::OrderMailer.confirm_email_for_customer(order) }
    let!(:confirmation_email_for_shop) { Spree::OrderMailer.confirm_email_for_shop(order) }
    let!(:cancellation_email) { Spree::OrderMailer.cancel_email(order) }

    it "includes Voucher text with label" do
      expect(confirmation_email_for_customer.body).to include("Voucher (#{voucher.code}):")
      expect(confirmation_email_for_shop.body).to include("Voucher (#{voucher.code}):")
      expect(cancellation_email.body).to include("Voucher (#{voucher.code}):")
    end

    it "includes Shipping label" do
      expect(confirmation_email_for_customer.body).to include("Shipping:")
      expect(confirmation_email_for_shop.body).to include("Shipping:")
      expect(cancellation_email.body).to include("Shipping:")
    end
  end
end
